#include "wx/wxprec.h"
#include "wx/richtext/richtextbuffer.h"
#include "wx/settings.h"
#include "wx/filename.h"
#include "wx/clipbrd.h"
#include "wx/wfstream.h"
#include "wx/mstream.h"
#include "wx/sstream.h"
#include "wx/textfile.h"
#include "wx/richtext/richtextctrl.h"
#include "wx/richtext/richtextstyles.h"
#include "wx/listimpl.cpp"

WX_DEFINE_LIST( wxRichTextObjectList )
WX_DEFINE_LIST( wxRichTextLineList )

// Switch off if the platform doesn't like it for some reason
#define wxRICHTEXT_USE_OPTIMIZED_DRAWING 1

const wxChar wxRichTextLineBreakChar = ( wxChar ) 29;

// Use GetPartialTextExtents for platforms that support it natively
#define wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS 1

// Use global array for binary compatibility; in 2.9+ it will be done more elegantly
#if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
wxArrayInt g_GlobalPartialTextExtents;
bool       g_UseGlobalPartialTextExtents = false;
#endif


// Helpers for efficiency

inline void wxCheckSetFont( wxDC& dc, const wxFont& font ) {
  const wxFont& font1 = dc.GetFont();
  if( font1.IsOk() && font.IsOk() ) {
    if( font1.GetPointSize() == font.GetPointSize() &&
        font1.GetFamily() == font.GetFamily() &&
        font1.GetStyle() == font.GetStyle() &&
        font1.GetWeight() == font.GetWeight() &&
        font1.GetUnderlined() == font.GetUnderlined() &&
        font1.GetFaceName() == font.GetFaceName() ) {
      return;
    }
  }
  dc.SetFont( font );
}

inline void wxCheckSetPen( wxDC& dc, const wxPen& pen ) {
  const wxPen& pen1 = dc.GetPen();
  if( pen1.IsOk() && pen.IsOk() ) {
    if( pen1.GetWidth() == pen.GetWidth() &&
        pen1.GetStyle() == pen.GetStyle() &&
        pen1.GetColour() == pen.GetColour() ) {
      return;
    }
  }
  dc.SetPen( pen );
}

inline void wxCheckSetBrush( wxDC& dc, const wxBrush& brush ) {
  const wxBrush& brush1 = dc.GetBrush();
  if( brush1.IsOk() && brush.IsOk() ) {
    if( brush1.GetStyle() == brush.GetStyle() &&
        brush1.GetColour() == brush.GetColour() ) {
      return;
    }
  }
  dc.SetBrush( brush );
}

inline void wxFontSetPointSize( wxFont& font, int pointSize ) {
  if( font.Ok() && font.GetPointSize() != pointSize ) {
    wxFont tempFont( pointSize, font.GetFamily(), font.GetStyle(), font.GetWeight(), font.GetUnderlined(), font.GetFaceName(), font.GetEncoding() );
    font = tempFont;
  }
}

inline void wxFontSetStyle( wxFont& font, int fontStyle ) {
  if( font.Ok() && font.GetStyle() != fontStyle ) {
    wxFont tempFont( font.GetPointSize(), font.GetFamily(), fontStyle, font.GetWeight(), font.GetUnderlined(), font.GetFaceName(), font.GetEncoding() );
    font = tempFont;
  }
}

inline void wxFontSetWeight( wxFont& font, int fontWeight ) {
  if( font.Ok() && font.GetWeight() != fontWeight ) {
    wxFont tempFont( font.GetPointSize(), font.GetFamily(), font.GetStyle(), fontWeight, font.GetUnderlined(), font.GetFaceName(), font.GetEncoding() );
    font = tempFont;
  }
}

inline void wxFontSetUnderlined( wxFont& font, bool underlined ) {
  if( font.Ok() && font.GetUnderlined() != underlined ) {
    wxFont tempFont( font.GetPointSize(), font.GetFamily(), font.GetStyle(), font.GetWeight(), underlined, font.GetFaceName(), font.GetEncoding() );
    font = tempFont;
  }
}

inline void wxFontSetFaceName( wxFont& font, const wxString& faceName ) {
  if( font.Ok() && font.GetFaceName() != faceName ) {
    wxFont tempFont( font.GetPointSize(), font.GetFamily(), font.GetStyle(), font.GetWeight(), font.GetUnderlined(), faceName, font.GetEncoding() );
    font = tempFont;
  }
}

IMPLEMENT_CLASS( wxRichTextObject, wxObject )

wxRichTextObject::wxRichTextObject( wxRichTextObject* parent ) {
  m_dirty = false;
  m_refCount = 1;
  m_parent = parent;
  m_leftMargin = 0;
  m_rightMargin = 0;
  m_topMargin = 0;
  m_bottomMargin = 0;
  m_descent = 0;
}

wxRichTextObject::~wxRichTextObject() {
}

void wxRichTextObject::Dereference() {
  m_refCount --;
  if( m_refCount <= 0 ) {
    delete this;
  }
}

void wxRichTextObject::Copy( const wxRichTextObject& obj ) {
  m_size = obj.m_size;
  m_pos = obj.m_pos;
  m_dirty = obj.m_dirty;
  m_range = obj.m_range;
  m_attributes = obj.m_attributes;
  m_descent = obj.m_descent;
}

void wxRichTextObject::SetMargins( int margin ) {
  m_leftMargin = m_rightMargin = m_topMargin = m_bottomMargin = margin;
}

void wxRichTextObject::SetMargins( int leftMargin, int rightMargin, int topMargin, int bottomMargin ) {
  m_leftMargin = leftMargin;
  m_rightMargin = rightMargin;
  m_topMargin = topMargin;
  m_bottomMargin = bottomMargin;
}

int wxRichTextObject::ConvertTenthsMMToPixels( wxDC& dc, int units ) {
  int p = ConvertTenthsMMToPixels( dc.GetPPI().x, units );
  wxRichTextBuffer* buffer = GetBuffer();
  if( buffer ) {
    p = ( int )( ( double )p / buffer->GetScale() );
  }
  return p;
}

int wxRichTextObject::ConvertTenthsMMToPixels( int ppi, int units ) {
  double pixels = ( ( double ) units * ( double )ppi ) / 254.1;
  return ( int ) pixels;
}

void wxRichTextObject::Dump( wxTextOutputStream& stream ) {
  stream << GetClassInfo()->GetClassName() << wxT( "\n" );
  stream << wxString::Format( wxT( "Size: %d,%d. Position: %d,%d, Range: %ld,%ld" ), m_size.x, m_size.y, m_pos.x, m_pos.y, m_range.GetStart(), m_range.GetEnd() ) << wxT( "\n" );
  stream << wxString::Format( wxT( "Text colour: %d,%d,%d." ), ( int ) m_attributes.GetTextColour().Red(), ( int ) m_attributes.GetTextColour().Green(), ( int ) m_attributes.GetTextColour().Blue() ) << wxT( "\n" );
}

wxRichTextBuffer* wxRichTextObject::GetBuffer() const {
  const wxRichTextObject* obj = this;
  while( obj && !obj->IsKindOf( CLASSINFO( wxRichTextBuffer ) ) ) {
    obj = obj->GetParent();
  }
  return wxDynamicCast( obj, wxRichTextBuffer );
}

IMPLEMENT_CLASS( wxRichTextCompositeObject, wxRichTextObject )

wxRichTextCompositeObject::wxRichTextCompositeObject( wxRichTextObject* parent ):
  wxRichTextObject( parent ) {
}

wxRichTextCompositeObject::~wxRichTextCompositeObject() {
  DeleteChildren();
}

wxRichTextObject* wxRichTextCompositeObject::GetChild( size_t n ) const {
  wxASSERT( n < m_children.GetCount() );
  return m_children.Item( n )->GetData();
}

size_t wxRichTextCompositeObject::AppendChild( wxRichTextObject* child ) {
  m_children.Append( child );
  child->SetParent( this );
  return m_children.GetCount() - 1;
}

bool wxRichTextCompositeObject::InsertChild( wxRichTextObject* child, wxRichTextObject* inFrontOf ) {
  if( inFrontOf ) {
    wxRichTextObjectList::compatibility_iterator node = m_children.Find( inFrontOf );
    m_children.Insert( node, child );
  } else
  { m_children.Insert( child ); }
  child->SetParent( this );
  return true;
}

bool wxRichTextCompositeObject::RemoveChild( wxRichTextObject* child, bool deleteChild ) {
  wxRichTextObjectList::compatibility_iterator node = m_children.Find( child );
  if( node ) {
    wxRichTextObject* obj = node->GetData();
    m_children.Erase( node );
    if( deleteChild ) {
      delete obj;
    }
    return true;
  }
  return false;
}

bool wxRichTextCompositeObject::DeleteChildren() {
  wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
  while( node ) {
    wxRichTextObjectList::compatibility_iterator oldNode = node;
    wxRichTextObject* child = node->GetData();
    child->Dereference();
    node = node->GetNext();
    m_children.Erase( oldNode );
  }
  return true;
}

size_t wxRichTextCompositeObject::GetChildCount() const {
  return m_children.GetCount();
}

void wxRichTextCompositeObject::Copy( const wxRichTextCompositeObject& obj ) {
  wxRichTextObject::Copy( obj );
  DeleteChildren();
  wxRichTextObjectList::compatibility_iterator node = obj.m_children.GetFirst();
  while( node ) {
    wxRichTextObject* child = node->GetData();
    wxRichTextObject* newChild = child->Clone();
    newChild->SetParent( this );
    m_children.Append( newChild );
    node = node->GetNext();
  }
}

int wxRichTextCompositeObject::HitTest( wxDC& dc, const wxPoint& pt, long& textPosition ) {
  wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
  while( node ) {
    wxRichTextObject* child = node->GetData();
    int ret = child->HitTest( dc, pt, textPosition );
    if( ret != wxRICHTEXT_HITTEST_NONE ) {
      return ret;
    }
    node = node->GetNext();
  }
  textPosition = GetRange().GetEnd() - 1;
  return wxRICHTEXT_HITTEST_AFTER | wxRICHTEXT_HITTEST_OUTSIDE;
}

bool wxRichTextCompositeObject::FindPosition( wxDC& dc, long index, wxPoint& pt, int* height, bool forceLineStart ) {
  wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
  while( node ) {
    wxRichTextObject* child = node->GetData();
    if( child->FindPosition( dc, index, pt, height, forceLineStart ) ) {
      return true;
    }
    node = node->GetNext();
  }
  return false;
}

void wxRichTextCompositeObject::CalculateRange( long start, long& end ) {
  long current = start;
  long lastEnd = current;
  wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
  while( node ) {
    wxRichTextObject* child = node->GetData();
    long childEnd = 0;
    child->CalculateRange( current, childEnd );
    lastEnd = childEnd;
    current = childEnd + 1;
    node = node->GetNext();
  }
  end = lastEnd;
  if( m_children.GetCount() == 0 ) {
    end --;
  }
  m_range.SetRange( start, end );
}

bool wxRichTextCompositeObject::DeleteRange( const wxRichTextRange& range ) {
  wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
  while( node ) {
    wxRichTextObject* obj = ( wxRichTextObject* ) node->GetData();
    wxRichTextObjectList::compatibility_iterator next = node->GetNext();
    if( !obj->GetRange().IsOutside( range ) ) {
      obj->DeleteRange( range );
      if( obj->IsEmpty() ||
          ( range.GetStart() <= obj->GetRange().GetStart() && range.GetEnd() >= obj->GetRange().GetEnd() ) ) {
        RemoveChild( obj, true );
      }
    }
    node = next;
  }
  return true;
}

wxString wxRichTextCompositeObject::GetTextForRange( const wxRichTextRange& range ) const {
  wxString text;
  wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
  while( node ) {
    wxRichTextObject* child = node->GetData();
    wxRichTextRange childRange = range;
    if( !child->GetRange().IsOutside( range ) ) {
      childRange.LimitTo( child->GetRange() );
      wxString childText = child->GetTextForRange( childRange );
      text += childText;
    }
    node = node->GetNext();
  }
  return text;
}

bool wxRichTextCompositeObject::Defragment() {
  wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
  while( node ) {
    wxRichTextObject* child = node->GetData();
    wxRichTextCompositeObject* composite = wxDynamicCast( child, wxRichTextCompositeObject );
    if( composite ) {
      composite->Defragment();
    }
    if( node->GetNext() ) {
      wxRichTextObject* nextChild = node->GetNext()->GetData();
      if( child->CanMerge( nextChild ) && child->Merge( nextChild ) ) {
        nextChild->Dereference();
        m_children.Erase( node->GetNext() );
      } else
      { node = node->GetNext(); }
    } else
    { node = node->GetNext(); }
  }
  return true;
}

void wxRichTextCompositeObject::Dump( wxTextOutputStream& stream ) {
  wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
  while( node ) {
    wxRichTextObject* child = node->GetData();
    child->Dump( stream );
    node = node->GetNext();
  }
}

IMPLEMENT_DYNAMIC_CLASS( wxRichTextBox, wxRichTextCompositeObject )

wxRichTextBox::wxRichTextBox( wxRichTextObject* parent ):
  wxRichTextCompositeObject( parent ) {
}

bool wxRichTextBox::Draw( wxDC& dc, const wxRichTextRange& range, const wxRichTextRange& selectionRange, const wxRect& ( rect ), int descent, int style ) {
  wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
  while( node ) {
    wxRichTextObject* child = node->GetData();
    wxRect childRect = wxRect( child->GetPosition(), child->GetCachedSize() );
    child->Draw( dc, range, selectionRange, childRect, descent, style );
    node = node->GetNext();
  }
  return true;
}

bool wxRichTextBox::Layout( wxDC& dc, const wxRect& rect, int style ) {
  wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
  while( node ) {
    wxRichTextObject* child = node->GetData();
    child->Layout( dc, rect, style );
    node = node->GetNext();
  }
  m_dirty = false;
  return true;
}

bool wxRichTextBox::GetRangeSize( const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, int flags, wxPoint position ) const {
  wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
  if( node ) {
    wxRichTextObject* child = node->GetData();
    return child->GetRangeSize( range, size, descent, dc, flags, position );
  } else
  { return false; }
}

void wxRichTextBox::Copy( const wxRichTextBox& obj ) {
  wxRichTextCompositeObject::Copy( obj );
}

IMPLEMENT_DYNAMIC_CLASS( wxRichTextParagraphLayoutBox, wxRichTextBox )

wxRichTextParagraphLayoutBox::wxRichTextParagraphLayoutBox( wxRichTextObject* parent ):
  wxRichTextBox( parent ) {
  Init();
}

void wxRichTextParagraphLayoutBox::Init() {
  m_ctrl = NULL;
  m_range = wxRichTextRange( 0, -1 );
  m_invalidRange.SetRange( -1, -1 );
  m_leftMargin = 4;
  m_rightMargin = 4;
  m_topMargin = 4;
  m_bottomMargin = 4;
  m_partialParagraph = false;
}

bool wxRichTextParagraphLayoutBox::Draw( wxDC& dc, const wxRichTextRange& range, const wxRichTextRange& selectionRange, const wxRect& rect, int descent, int style ) {
  wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
  while( node ) {
    wxRichTextParagraph* child = wxDynamicCast( node->GetData(), wxRichTextParagraph );
    wxASSERT( child != NULL );
    if( child && !child->GetRange().IsOutside( range ) ) {
      wxRect childRect( child->GetPosition(), child->GetCachedSize() );
      if( ( ( style & wxRICHTEXT_DRAW_IGNORE_CACHE ) == 0 ) && childRect.GetTop() > rect.GetBottom() ) {
        break;
      } else if( ( ( style & wxRICHTEXT_DRAW_IGNORE_CACHE ) == 0 ) && childRect.GetBottom() < rect.GetTop() ) {
      } else
      { child->Draw( dc, range, selectionRange, rect, descent, style ); }
    }
    node = node->GetNext();
  }
  return true;
}

bool wxRichTextParagraphLayoutBox::Layout( wxDC& dc, const wxRect& rect, int style ) {
  wxRect availableSpace;
  bool formatRect = ( style & wxRICHTEXT_LAYOUT_SPECIFIED_RECT ) == wxRICHTEXT_LAYOUT_SPECIFIED_RECT;
  if( formatRect ) {
    availableSpace = wxRect( 0 + m_leftMargin,
                             0 + m_topMargin,
                             rect.width - m_leftMargin - m_rightMargin,
                             rect.height );
    long startPos = 0;
    wxRichTextLine* line = GetLineAtYPosition( rect.y );
    if( line ) {
      startPos = line->GetAbsoluteRange().GetStart();
    }
    Invalidate( wxRichTextRange( startPos, GetRange().GetEnd() ) );
  } else
    availableSpace = wxRect( rect.x + m_leftMargin,
                             rect.y + m_topMargin,
                             rect.width - m_leftMargin - m_rightMargin,
                             rect.height - m_topMargin - m_bottomMargin );
  int maxWidth = 0;
  wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
  bool layoutAll = true;
  wxRichTextRange invalidRange = GetInvalidRange( true );
  if( invalidRange == wxRICHTEXT_NONE && !formatRect ) {
    return true;
  }
  if( invalidRange == wxRICHTEXT_ALL ) {
    layoutAll = true;
  } else if( invalidRange.GetStart() >= GetRange().GetStart() ) {
    wxRichTextParagraph* firstParagraph = GetParagraphAtPosition( invalidRange.GetStart() );
    if( firstParagraph ) {
      wxRichTextObjectList::compatibility_iterator firstNode = m_children.Find( firstParagraph );
      wxRichTextObjectList::compatibility_iterator previousNode;
      if( firstNode ) {
        previousNode = firstNode->GetPrevious();
      }
      if( firstNode ) {
        if( previousNode ) {
          wxRichTextParagraph* previousParagraph = wxDynamicCast( previousNode->GetData(), wxRichTextParagraph );
          availableSpace.y = previousParagraph->GetPosition().y + previousParagraph->GetCachedSize().y;
        }
        node = firstNode;
        layoutAll = false;
      }
    }
  }
  bool forceQuickLayout = false;
  while( node ) {
    wxRichTextParagraph* child = wxDynamicCast( node->GetData(), wxRichTextParagraph );
    wxCHECK_MSG( child, false, _T( "Unknown object in layout" ) );
    if( !forceQuickLayout &&
        ( layoutAll ||
          child->GetLines().IsEmpty() ||
          !child->GetRange().IsOutside( invalidRange ) ) ) {
      child->Layout( dc, availableSpace, style );
      availableSpace.y += child->GetCachedSize().y;
      maxWidth = wxMax( maxWidth, child->GetCachedSize().x );
      if( formatRect && child->GetPosition().y > rect.GetBottom() ) {
        forceQuickLayout = true;
      }
    } else {
      int inc = availableSpace.y - child->GetPosition().y;
      while( node ) {
        wxRichTextParagraph* child = wxDynamicCast( node->GetData(), wxRichTextParagraph );
        if( child ) {
          if( child->GetLines().GetCount() == 0 ) {
            child->Layout( dc, availableSpace, style );
          } else
          { child->SetPosition( wxPoint( child->GetPosition().x, child->GetPosition().y + inc ) ); }
          availableSpace.y += child->GetCachedSize().y;
          maxWidth = wxMax( maxWidth, child->GetCachedSize().x );
        }
        node = node->GetNext();
      }
      break;
    }
    node = node->GetNext();
  }
  SetCachedSize( wxSize( maxWidth, availableSpace.y ) );
  m_dirty = false;
  m_invalidRange = wxRICHTEXT_NONE;
  return true;
}

void wxRichTextParagraphLayoutBox::Copy( const wxRichTextParagraphLayoutBox& obj ) {
  wxRichTextBox::Copy( obj );
  m_partialParagraph = obj.m_partialParagraph;
  m_defaultAttributes = obj.m_defaultAttributes;
}

bool wxRichTextParagraphLayoutBox::GetRangeSize( const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, int flags, wxPoint position ) const {
  wxSize sz;
  wxRichTextObjectList::compatibility_iterator startPara = wxRichTextObjectList::compatibility_iterator();
  wxRichTextObjectList::compatibility_iterator endPara = wxRichTextObjectList::compatibility_iterator();
  wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
  while( node ) {
    wxRichTextObject* child = node->GetData();
    const wxRichTextRange& r = child->GetRange();
    if( r.GetStart() <= range.GetStart() && r.GetEnd() >= range.GetStart() ) {
      startPara = node;
      break;
    }
    node = node->GetNext();
  }
  node = m_children.GetFirst();
  while( node ) {
    // child is a paragraph
    wxRichTextObject* child = node->GetData();
    const wxRichTextRange& r = child->GetRange();
    if( r.GetStart() <= range.GetEnd() && r.GetEnd() >= range.GetEnd() ) {
      endPara = node;
      break;
    }
    node = node->GetNext();
  }
  if( !startPara || !endPara ) {
    return false;
  }
  // Now we can add up the sizes
  for( node = startPara; node ; node = node->GetNext() ) {
    // child is a paragraph
    wxRichTextObject* child = node->GetData();
    const wxRichTextRange& childRange = child->GetRange();
    wxRichTextRange rangeToFind = range;
    rangeToFind.LimitTo( childRange );
    wxSize childSize;
    int childDescent = 0;
    child->GetRangeSize( rangeToFind, childSize, childDescent, dc, flags, position );
    descent = wxMax( childDescent, descent );
    sz.x = wxMax( sz.x, childSize.x );
    sz.y += childSize.y;
    if( node == endPara ) {
      break;
    }
  }
  size = sz;
  return true;
}

wxRichTextParagraph* wxRichTextParagraphLayoutBox::GetParagraphAtPosition( long pos, bool caretPosition ) const {
  if( caretPosition ) {
    pos ++;
  }
  wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
  while( node ) {
    wxRichTextParagraph* child = wxDynamicCast( node->GetData(), wxRichTextParagraph );
    wxASSERT( child != NULL );
    if( child->GetRange().Contains( pos ) ) {
      return child;
    }
    node = node->GetNext();
  }
  return NULL;
}

wxRichTextLine* wxRichTextParagraphLayoutBox::GetLineAtPosition( long pos, bool caretPosition ) const {
  if( caretPosition ) {
    pos ++;
  }
  // First find the first paragraph whose starting position is within the range.
  wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
  while( node ) {
    wxRichTextObject* obj = ( wxRichTextObject* ) node->GetData();
    if( obj->GetRange().Contains( pos ) ) {
      // child is a paragraph
      wxRichTextParagraph* child = wxDynamicCast( obj, wxRichTextParagraph );
      wxASSERT( child != NULL );
      wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
      while( node2 ) {
        wxRichTextLine* line = node2->GetData();
        wxRichTextRange range = line->GetAbsoluteRange();
        if( range.Contains( pos ) ||
            // If the position is end-of-paragraph, then return the last line of
            // of the paragraph.
            ( ( range.GetEnd() == child->GetRange().GetEnd() - 1 ) && ( pos == child->GetRange().GetEnd() ) ) ) {
          return line;
        }
        node2 = node2->GetNext();
      }
    }
    node = node->GetNext();
  }
  int lineCount = GetLineCount();
  if( lineCount > 0 ) {
    return GetLineForVisibleLineNumber( lineCount - 1 );
  } else
  { return NULL; }
}

wxRichTextLine* wxRichTextParagraphLayoutBox::GetLineAtYPosition( int y ) const {
  wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
  while( node ) {
    wxRichTextParagraph* child = wxDynamicCast( node->GetData(), wxRichTextParagraph );
    wxASSERT( child != NULL );
    wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
    while( node2 ) {
      wxRichTextLine* line = node2->GetData();
      wxRect rect( line->GetRect() );
      if( y <= rect.GetBottom() ) {
        return line;
      }
      node2 = node2->GetNext();
    }
    node = node->GetNext();
  }
  // Return last line
  int lineCount = GetLineCount();
  if( lineCount > 0 ) {
    return GetLineForVisibleLineNumber( lineCount - 1 );
  } else
  { return NULL; }
}

int wxRichTextParagraphLayoutBox::GetLineCount() const {
  int count = 0;
  wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
  while( node ) {
    wxRichTextParagraph* child = wxDynamicCast( node->GetData(), wxRichTextParagraph );
    wxASSERT( child != NULL );
    count += child->GetLines().GetCount();
    node = node->GetNext();
  }
  return count;
}

wxRichTextParagraph* wxRichTextParagraphLayoutBox::GetParagraphForLine( wxRichTextLine* line ) const {
  return GetParagraphAtPosition( line->GetAbsoluteRange().GetStart() );
}

wxSize wxRichTextParagraphLayoutBox::GetLineSizeAtPosition( long pos, bool caretPosition ) const {
  wxRichTextLine* line = GetLineAtPosition( pos, caretPosition );
  if( line ) {
    return line->GetSize();
  } else
  { return wxSize( 0, 0 ); }
}


wxRichTextRange wxRichTextParagraphLayoutBox::AddParagraph( const wxString& text, wxTextAttrEx* paraStyle ) {
  wxTextAttrEx defaultCharStyle;
  wxTextAttrEx defaultParaStyle;
  if( GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet() ) {
    wxRichTextParagraphStyleDefinition* def = GetStyleSheet()->FindParagraphStyle( GetDefaultStyle().GetParagraphStyleName() );
    if( def ) {
      defaultParaStyle = def->GetStyleMergedWithBase( GetStyleSheet() );
    }
  } else
  { wxRichTextSplitParaCharStyles( GetDefaultStyle(), defaultParaStyle, defaultCharStyle ); }
  wxTextAttrEx* pStyle = paraStyle ? paraStyle : ( wxTextAttrEx* ) & defaultParaStyle;
  wxTextAttrEx* cStyle = & defaultCharStyle;
  wxRichTextParagraph* para = new wxRichTextParagraph( text, this, pStyle, cStyle );
  AppendChild( para );
  UpdateRanges();
  SetDirty( true );
  return para->GetRange();
}

wxRichTextRange wxRichTextParagraphLayoutBox::AddParagraphs( const wxString& text, wxTextAttrEx* paraStyle ) {
  wxTextAttrEx defaultCharStyle;
  wxTextAttrEx defaultParaStyle;
  if( GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet() ) {
    wxRichTextParagraphStyleDefinition* def = GetStyleSheet()->FindParagraphStyle( GetDefaultStyle().GetParagraphStyleName() );
    if( def ) {
      defaultParaStyle = def->GetStyleMergedWithBase( GetStyleSheet() );
    }
  } else
  { wxRichTextSplitParaCharStyles( GetDefaultStyle(), defaultParaStyle, defaultCharStyle ); }
  wxTextAttrEx* pStyle = paraStyle ? paraStyle : ( wxTextAttrEx* ) & defaultParaStyle;
  wxTextAttrEx* cStyle = & defaultCharStyle;
  wxRichTextParagraph* firstPara = NULL;
  wxRichTextParagraph* lastPara = NULL;
  wxRichTextRange range( -1, -1 );
  size_t i = 0;
  size_t len = text.length();
  wxString line;
  wxRichTextParagraph* para = new wxRichTextParagraph( wxEmptyString, this, pStyle, cStyle );
  AppendChild( para );
  firstPara = para;
  lastPara = para;
  while( i < len ) {
    wxChar ch = text[i];
    if( ch == wxT( '\n' ) || ch == wxT( '\r' ) ) {
      if( i != ( len - 1 ) ) {
        wxRichTextPlainText* plainText = ( wxRichTextPlainText* ) para->GetChildren().GetFirst()->GetData();
        plainText->SetText( line );
        para = new wxRichTextParagraph( wxEmptyString, this, pStyle, cStyle );
        AppendChild( para );
        lastPara = para;
        line = wxEmptyString;
      }
    } else
    { line += ch; }
    i ++;
  }
  if( !line.empty() ) {
    wxRichTextPlainText* plainText = ( wxRichTextPlainText* ) para->GetChildren().GetFirst()->GetData();
    plainText->SetText( line );
  }
  UpdateRanges();
  SetDirty( false );
  return wxRichTextRange( firstPara->GetRange().GetStart(), lastPara->GetRange().GetEnd() );
}

wxRichTextRange wxRichTextParagraphLayoutBox::AddImage( const wxImage& image, wxTextAttrEx* paraStyle ) {
  wxTextAttrEx defaultCharStyle;
  wxTextAttrEx defaultParaStyle;
  if( GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet() ) {
    wxRichTextParagraphStyleDefinition* def = GetStyleSheet()->FindParagraphStyle( GetDefaultStyle().GetParagraphStyleName() );
    if( def ) {
      defaultParaStyle = def->GetStyleMergedWithBase( GetStyleSheet() );
    }
  } else
  { wxRichTextSplitParaCharStyles( GetDefaultStyle(), defaultParaStyle, defaultCharStyle ); }
  wxTextAttrEx* pStyle = paraStyle ? paraStyle : ( wxTextAttrEx* ) & defaultParaStyle;
  wxTextAttrEx* cStyle = & defaultCharStyle;
  wxRichTextParagraph* para = new wxRichTextParagraph( this, pStyle );
  AppendChild( para );
  para->AppendChild( new wxRichTextImage( image, this, cStyle ) );
  UpdateRanges();
  SetDirty( true );
  return para->GetRange();
}

bool wxRichTextParagraphLayoutBox::InsertFragment( long position, wxRichTextParagraphLayoutBox& fragment ) {
  SetDirty( true );
  wxRichTextParagraph* para = GetParagraphAtPosition( position );
  if( para ) {
    wxTextAttrEx originalAttr = para->GetAttributes();
    wxRichTextObjectList::compatibility_iterator node = m_children.Find( para );
    wxRichTextObject* nextObject = para->SplitAt( position );
    if( fragment.GetPartialParagraph() && fragment.GetChildren().GetCount() == 1 ) {
      wxRichTextObjectList::compatibility_iterator firstParaNode = fragment.GetChildren().GetFirst();
      if( !firstParaNode ) {
        return false;
      }
      wxRichTextParagraph* firstPara = wxDynamicCast( firstParaNode->GetData(), wxRichTextParagraph );
      wxASSERT( firstPara != NULL );
      wxRichTextObjectList::compatibility_iterator objectNode = firstPara->GetChildren().GetFirst();
      while( objectNode ) {
        wxRichTextObject* newObj = objectNode->GetData()->Clone();
        if( !nextObject ) {
          para->AppendChild( newObj );
        } else {
          para->InsertChild( newObj, nextObject );
        }
        objectNode = objectNode->GetNext();
      }
      return true;
    } else {
      wxList savedObjects;
      if( nextObject ) {
        para->MoveToList( nextObject, savedObjects );
      }
      wxRichTextObjectList::compatibility_iterator firstParaNode = fragment.GetChildren().GetFirst();
      if( !firstParaNode ) {
        return false;
      }
      wxRichTextParagraph* firstPara = wxDynamicCast( firstParaNode->GetData(), wxRichTextParagraph );
      wxASSERT( firstPara != NULL );
      if( !( fragment.GetAttributes().GetFlags() & wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE ) ) {
        para->SetAttributes( firstPara->GetAttributes() );
      }
      wxTextAttrEx emptyParagraphAttributes;
      wxRichTextObjectList::compatibility_iterator objectNode = firstPara->GetChildren().GetFirst();
      if( objectNode && firstPara->GetChildren().GetCount() == 1 && objectNode->GetData()->IsEmpty() ) {
        emptyParagraphAttributes = objectNode->GetData()->GetAttributes();
      }
      while( objectNode ) {
        wxRichTextObject* newObj = objectNode->GetData()->Clone();
        para->AppendChild( newObj );
        objectNode = objectNode->GetNext();
      }
      wxRichTextObjectList::compatibility_iterator nextParagraphNode = node->GetNext();
      wxRichTextObject* nextParagraph = NULL;
      if( nextParagraphNode ) {
        nextParagraph = nextParagraphNode->GetData();
      }
      wxRichTextObjectList::compatibility_iterator i = fragment.GetChildren().GetFirst()->GetNext();
      wxRichTextParagraph* finalPara = para;
      bool needExtraPara = ( !i || !fragment.GetPartialParagraph() );
      while( i ) {
        wxRichTextParagraph* para = wxDynamicCast( i->GetData(), wxRichTextParagraph );
        wxASSERT( para != NULL );
        finalPara = ( wxRichTextParagraph* ) para->Clone();
        if( nextParagraph ) {
          InsertChild( finalPara, nextParagraph );
        } else
        { AppendChild( finalPara ); }
        i = i->GetNext();
      }
      if( needExtraPara ) {
        finalPara = new wxRichTextParagraph;
        if( nextParagraph ) {
          InsertChild( finalPara, nextParagraph );
        } else
        { AppendChild( finalPara ); }
      }
      if( finalPara ) {
        if( nextObject ) {
          finalPara->MoveFromList( savedObjects );
        }
        if( finalPara->GetChildCount() == 0 ) {
          wxRichTextPlainText* text = new wxRichTextPlainText( wxEmptyString );
          text->SetAttributes( emptyParagraphAttributes );
          finalPara->AppendChild( text );
        }
      }
      if( ( fragment.GetAttributes().GetFlags() & wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE ) && firstPara ) {
        finalPara->SetAttributes( firstPara->GetAttributes() );
      } else if( finalPara && finalPara != para ) {
        finalPara->SetAttributes( originalAttr );
      }
      return true;
    }
  } else {
    wxRichTextObjectList::compatibility_iterator i = fragment.GetChildren().GetFirst();
    while( i ) {
      wxRichTextParagraph* para = wxDynamicCast( i->GetData(), wxRichTextParagraph );
      wxASSERT( para != NULL );
      AppendChild( para->Clone() );
      i = i->GetNext();
    }
    return true;
  }
}

bool wxRichTextParagraphLayoutBox::CopyFragment( const wxRichTextRange& range, wxRichTextParagraphLayoutBox& fragment ) {
  wxRichTextObjectList::compatibility_iterator i = GetChildren().GetFirst();
  while( i ) {
    wxRichTextParagraph* para = wxDynamicCast( i->GetData(), wxRichTextParagraph );
    wxASSERT( para != NULL );
    if( !para->GetRange().IsOutside( range ) ) {
      fragment.AppendChild( para->Clone() );
    }
    i = i->GetNext();
  }
  if( !fragment.IsEmpty() ) {
    wxRichTextRange topTailRange( range );
    wxRichTextParagraph* firstPara = wxDynamicCast( fragment.GetChildren().GetFirst()->GetData(), wxRichTextParagraph );
    wxASSERT( firstPara != NULL );
    if( topTailRange.GetStart() > firstPara->GetRange().GetStart() ) {
      wxRichTextRange r( firstPara->GetRange().GetStart(), topTailRange.GetStart() - 1 );
      firstPara->DeleteRange( r );
      long end;
      fragment.CalculateRange( firstPara->GetRange().GetStart(), end );
      topTailRange.SetEnd( topTailRange.GetEnd() - r.GetLength() );
    }
    wxRichTextParagraph* lastPara = wxDynamicCast( fragment.GetChildren().GetLast()->GetData(), wxRichTextParagraph );
    wxASSERT( lastPara != NULL );
    if( topTailRange.GetEnd() < ( lastPara->GetRange().GetEnd() - 1 ) ) {
      wxRichTextRange r( topTailRange.GetEnd() + 1, lastPara->GetRange().GetEnd() - 1 );
      lastPara->DeleteRange( r );
      long end;
      fragment.CalculateRange( firstPara->GetRange().GetStart(), end );
      fragment.SetPartialParagraph( true );
    } else {
      if( topTailRange.GetEnd() == ( lastPara->GetRange().GetEnd() - 1 ) )
      { fragment.SetPartialParagraph( true ); }
      else
      { fragment.SetPartialParagraph( false ); }
    }
  }
  return true;
}

long wxRichTextParagraphLayoutBox::GetVisibleLineNumber( long pos, bool caretPosition, bool startOfLine ) const {
  if( caretPosition ) {
    pos ++;
  }
  int lineCount = 0;
  wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
  while( node ) {
    wxRichTextParagraph* child = wxDynamicCast( node->GetData(), wxRichTextParagraph );
    wxASSERT( child != NULL );
    if( child->GetRange().Contains( pos ) || pos == child->GetRange().GetStart() ) {
      wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
      while( node2 ) {
        wxRichTextLine* line = node2->GetData();
        wxRichTextRange lineRange = line->GetAbsoluteRange();
        if( lineRange.Contains( pos ) ) {
          if( lineRange.GetStart() == pos && !startOfLine && child->GetRange().GetStart() != pos ) {
            return lineCount - 1;
          } else
          { return lineCount; }
        }
        lineCount ++;
        node2 = node2->GetNext();
      }
      return lineCount - 1;
    } else
    { lineCount += child->GetLines().GetCount(); }
    node = node->GetNext();
  }
  return -1;
}

wxRichTextLine* wxRichTextParagraphLayoutBox::GetLineForVisibleLineNumber( long lineNumber ) const {
  int lineCount = 0;
  wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
  while( node ) {
    wxRichTextParagraph* child = wxDynamicCast( node->GetData(), wxRichTextParagraph );
    wxASSERT( child != NULL );
    if( lineNumber < ( int )( child->GetLines().GetCount() + lineCount ) ) {
      wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
      while( node2 ) {
        wxRichTextLine* line = node2->GetData();
        if( lineCount == lineNumber ) {
          return line;
        }
        lineCount ++;
        node2 = node2->GetNext();
      }
    } else
    { lineCount += child->GetLines().GetCount(); }
    node = node->GetNext();
  }
  return NULL;
}

bool wxRichTextParagraphLayoutBox::DeleteRange( const wxRichTextRange& range ) {
  wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
  wxRichTextParagraph* firstPara = NULL;
  while( node ) {
    wxRichTextParagraph* obj = wxDynamicCast( node->GetData(), wxRichTextParagraph );
    wxASSERT( obj != NULL );
    wxRichTextObjectList::compatibility_iterator next = node->GetNext();
    if( !obj->GetRange().IsOutside( range ) ) {
      wxRichTextRange thisRange = obj->GetRange();
      wxTextAttrEx thisAttr = obj->GetAttributes();
      obj->DeleteRange( range );
      if( range.GetStart() <= thisRange.GetStart() && range.GetEnd() >= thisRange.GetEnd() ) {
        RemoveChild( obj, true );
        obj = NULL;
      } else if( !firstPara ) {
        firstPara = obj;
      }
      if( range.GetEnd() <= thisRange.GetEnd() ) {
        wxRichTextParagraph* nextParagraph = NULL;
        if( ( range.GetEnd() < thisRange.GetEnd() ) && obj ) {
          nextParagraph = obj;
        } else {
          if( next ) {
            nextParagraph = wxDynamicCast( next->GetData(), wxRichTextParagraph );
          }
        }
        bool applyFinalParagraphStyle = firstPara && nextParagraph && nextParagraph != firstPara;
        wxTextAttrEx nextParaAttr;
        if( applyFinalParagraphStyle ) {
          if( range.GetStart() == range.GetEnd() && range.GetStart() == thisRange.GetEnd() ) {
            nextParaAttr = thisAttr;
          } else
          { nextParaAttr = nextParagraph->GetAttributes(); }
        }
        if( firstPara && nextParagraph && firstPara != nextParagraph ) {
          wxRichTextObjectList::compatibility_iterator node1 = nextParagraph->GetChildren().GetFirst();
          while( node1 ) {
            wxRichTextObject* obj1 = node1->GetData();
            firstPara->AppendChild( obj1 );
            wxRichTextObjectList::compatibility_iterator next1 = node1->GetNext();
            nextParagraph->GetChildren().Erase( node1 );
            node1 = next1;
          }
          RemoveChild( nextParagraph, true );
        }
        if( firstPara && firstPara->GetChildren().GetCount() == 0 ) {
          wxRichTextPlainText* text = new wxRichTextPlainText( wxEmptyString );
          firstPara->AppendChild( text );
        }
        if( applyFinalParagraphStyle ) {
          firstPara->SetAttributes( nextParaAttr );
        }
        return true;
      }
    }
    node = next;
  }
  return true;
}

wxString wxRichTextParagraphLayoutBox::GetTextForRange( const wxRichTextRange& range ) const {
  int lineCount = 0;
  wxString text;
  wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
  while( node ) {
    wxRichTextObject* child = node->GetData();
    if( !child->GetRange().IsOutside( range ) ) {
      wxRichTextRange childRange = range;
      childRange.LimitTo( child->GetRange() );
      wxString childText = child->GetTextForRange( childRange );
      text += childText;
      if( ( childRange.GetEnd() == child->GetRange().GetEnd() ) && node->GetNext() ) {
        text += wxT( "\n" );
      }
      lineCount ++;
    }
    node = node->GetNext();
  }
  return text;
}

wxString wxRichTextParagraphLayoutBox::GetText() const {
  return GetTextForRange( GetRange() );
}

wxRichTextParagraph* wxRichTextParagraphLayoutBox::GetParagraphAtLine( long paragraphNumber ) const {
  if( ( size_t ) paragraphNumber >= GetChildCount() ) {
    return NULL;
  }
  return ( wxRichTextParagraph* ) GetChild( ( size_t ) paragraphNumber );
}

int wxRichTextParagraphLayoutBox::GetParagraphLength( long paragraphNumber ) const {
  wxRichTextParagraph* para = GetParagraphAtLine( paragraphNumber );
  if( para ) {
    return para->GetRange().GetLength() - 1;
  } else
  { return 0; }
}

wxString wxRichTextParagraphLayoutBox::GetParagraphText( long paragraphNumber ) const {
  wxRichTextParagraph* para = GetParagraphAtLine( paragraphNumber );
  if( para ) {
    return para->GetTextForRange( para->GetRange() );
  } else
  { return wxEmptyString; }
}

long wxRichTextParagraphLayoutBox::XYToPosition( long x, long y ) const {
  wxRichTextParagraph* para = GetParagraphAtLine( y );
  if( para ) {
    return para->GetRange().GetStart() + x;
  } else
  { return -1; }
}

bool wxRichTextParagraphLayoutBox::PositionToXY( long pos, long* x, long* y ) const {
  wxRichTextParagraph* para = GetParagraphAtPosition( pos );
  if( para ) {
    int count = 0;
    wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
    while( node ) {
      wxRichTextObject* child = node->GetData();
      if( child == para ) {
        break;
      }
      count ++;
      node = node->GetNext();
    }
    *y = count;
    *x = pos - para->GetRange().GetStart();
    return true;
  } else
  { return false; }
}

wxRichTextObject* wxRichTextParagraphLayoutBox::GetLeafObjectAtPosition( long position ) const {
  wxRichTextParagraph* para = GetParagraphAtPosition( position );
  if( para ) {
    wxRichTextObjectList::compatibility_iterator node = para->GetChildren().GetFirst();
    while( node ) {
      wxRichTextObject* child = node->GetData();
      if( child->GetRange().Contains( position ) ) {
        return child;
      }
      node = node->GetNext();
    }
    if( position == para->GetRange().GetEnd() && para->GetChildCount() > 0 ) {
      return para->GetChildren().GetLast()->GetData();
    }
  }
  return NULL;
}

bool wxRichTextParagraphLayoutBox::SetStyle( const wxRichTextRange& range, const wxRichTextAttr& style, int flags ) {
  bool characterStyle = false;
  bool paragraphStyle = false;
  if( style.IsCharacterStyle() ) {
    characterStyle = true;
  }
  if( style.IsParagraphStyle() ) {
    paragraphStyle = true;
  }
  bool withUndo = ( ( flags & wxRICHTEXT_SETSTYLE_WITH_UNDO ) != 0 );
  bool applyMinimal = ( ( flags & wxRICHTEXT_SETSTYLE_OPTIMIZE ) != 0 );
  bool parasOnly = ( ( flags & wxRICHTEXT_SETSTYLE_PARAGRAPHS_ONLY ) != 0 );
  bool charactersOnly = ( ( flags & wxRICHTEXT_SETSTYLE_CHARACTERS_ONLY ) != 0 );
  bool resetExistingStyle = ( ( flags & wxRICHTEXT_SETSTYLE_RESET ) != 0 );
  bool removeStyle = ( ( flags & wxRICHTEXT_SETSTYLE_REMOVE ) != 0 );
  // Apply paragraph style first, if any
  wxRichTextAttr wholeStyle( style );
  if( !removeStyle && wholeStyle.HasParagraphStyleName() && GetStyleSheet() ) {
    wxRichTextParagraphStyleDefinition* def = GetStyleSheet()->FindParagraphStyle( wholeStyle.GetParagraphStyleName() );
    if( def ) {
      wxRichTextApplyStyle( wholeStyle, def->GetStyleMergedWithBase( GetStyleSheet() ) );
    }
  }
  // Limit the attributes to be set to the content to only character attributes.
  wxRichTextAttr characterAttributes( wholeStyle );
  characterAttributes.SetFlags( characterAttributes.GetFlags() & ( wxTEXT_ATTR_CHARACTER ) );
  if( !removeStyle && characterAttributes.HasCharacterStyleName() && GetStyleSheet() ) {
    wxRichTextCharacterStyleDefinition* def = GetStyleSheet()->FindCharacterStyle( characterAttributes.GetCharacterStyleName() );
    if( def ) {
      wxRichTextApplyStyle( characterAttributes, def->GetStyleMergedWithBase( GetStyleSheet() ) );
    }
  }
  // If we are associated with a control, make undoable; otherwise, apply immediately
  // to the data.
  bool haveControl = ( GetRichTextCtrl() != NULL );
  wxRichTextAction* action = NULL;
  if( haveControl && withUndo ) {
    action = new wxRichTextAction( NULL, _( "Change Style" ), wxRICHTEXT_CHANGE_STYLE, & GetRichTextCtrl()->GetBuffer(), GetRichTextCtrl() );
    action->SetRange( range );
    action->SetPosition( GetRichTextCtrl()->GetCaretPosition() );
  }
  wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
  while( node ) {
    wxRichTextParagraph* para = wxDynamicCast( node->GetData(), wxRichTextParagraph );
    wxASSERT( para != NULL );
    if( para && para->GetChildCount() > 0 ) {
      // Stop searching if we're beyond the range of interest
      if( para->GetRange().GetStart() > range.GetEnd() ) {
        break;
      }
      if( !para->GetRange().IsOutside( range ) ) {
        // We'll be using a copy of the paragraph to make style changes,
        // not updating the buffer directly.
        wxRichTextParagraph* newPara wxDUMMY_INITIALIZE( NULL );
        if( haveControl && withUndo ) {
          newPara = new wxRichTextParagraph( *para );
          action->GetNewParagraphs().AppendChild( newPara );
          // Also store the old ones for Undo
          action->GetOldParagraphs().AppendChild( new wxRichTextParagraph( *para ) );
        } else
        { newPara = para; }
        // If we're specifying paragraphs only, then we really mean character formatting
        // to be included in the paragraph style
        if( ( paragraphStyle || parasOnly ) && !charactersOnly ) {
          if( removeStyle ) {
            // Removes the given style from the paragraph
            wxRichTextRemoveStyle( newPara->GetAttributes(), style );
          } else if( resetExistingStyle ) {
            newPara->GetAttributes() = wholeStyle;
          } else {
            if( applyMinimal ) {
              // Only apply attributes that will make a difference to the combined
              // style as seen on the display
              wxRichTextAttr combinedAttr( para->GetCombinedAttributes() );
              wxRichTextApplyStyle( newPara->GetAttributes(), wholeStyle, & combinedAttr );
            } else
            { wxRichTextApplyStyle( newPara->GetAttributes(), wholeStyle ); }
          }
        }
        if( !parasOnly && ( characterStyle | charactersOnly ) && range.GetStart() != newPara->GetRange().GetEnd() ) {
          wxRichTextRange childRange( range );
          childRange.LimitTo( newPara->GetRange() );
          wxRichTextObject* firstObject wxDUMMY_INITIALIZE( NULL );
          wxRichTextObject* lastObject wxDUMMY_INITIALIZE( NULL );
          if( childRange.GetStart() == newPara->GetRange().GetStart() ) {
            firstObject = newPara->GetChildren().GetFirst()->GetData();
          } else
          { firstObject = newPara->SplitAt( range.GetStart() ); }
          // Increment by 1 because we're apply the style one _after_ the split point
          long splitPoint = childRange.GetEnd();
          if( splitPoint != newPara->GetRange().GetEnd() ) {
            splitPoint ++;
          }
          // Find last object
          if( splitPoint == newPara->GetRange().GetEnd() ) {
            lastObject = newPara->GetChildren().GetLast()->GetData();
          } else
            // lastObject is set as a side-effect of splitting. It's
            // returned as the object before the new object.
          { ( void ) newPara->SplitAt( splitPoint, & lastObject ); }
          wxASSERT( firstObject != NULL );
          wxASSERT( lastObject != NULL );
          if( !firstObject || !lastObject ) {
            continue;
          }
          wxRichTextObjectList::compatibility_iterator firstNode = newPara->GetChildren().Find( firstObject );
          wxRichTextObjectList::compatibility_iterator lastNode = newPara->GetChildren().Find( lastObject );
          wxASSERT( firstNode );
          wxASSERT( lastNode );
          wxRichTextObjectList::compatibility_iterator node2 = firstNode;
          while( node2 ) {
            wxRichTextObject* child = node2->GetData();
            if( removeStyle ) {
              // Removes the given style from the paragraph
              wxRichTextRemoveStyle( child->GetAttributes(), style );
            } else if( resetExistingStyle )
            { child->GetAttributes() = characterAttributes; }
            else {
              if( applyMinimal ) {
                // Only apply attributes that will make a difference to the combined
                // style as seen on the display
                wxRichTextAttr combinedAttr( newPara->GetCombinedAttributes( child->GetAttributes() ) );
                wxRichTextApplyStyle( child->GetAttributes(), characterAttributes, & combinedAttr );
              } else
              { wxRichTextApplyStyle( child->GetAttributes(), characterAttributes ); }
            }
            if( node2 == lastNode )
            { break; }
            node2 = node2->GetNext();
          }
        }
      }
    }
    node = node->GetNext();
  }
  // Do action, or delay it until end of batch.
  if( haveControl && withUndo ) {
    GetRichTextCtrl()->GetBuffer().SubmitAction( action );
  }
  return true;
}

/// Set text attributes
bool wxRichTextParagraphLayoutBox::SetStyle( const wxRichTextRange& range, const wxTextAttrEx& style, int flags ) {
  wxRichTextAttr richStyle = style;
  return SetStyle( range, richStyle, flags );
}

/// Get the text attributes for this position.
bool wxRichTextParagraphLayoutBox::GetStyle( long position, wxTextAttrEx& style ) {
  return DoGetStyle( position, style, true );
}

/// Get the text attributes for this position.
bool wxRichTextParagraphLayoutBox::GetStyle( long position, wxRichTextAttr& style ) {
  wxTextAttrEx textAttrEx( style );
  if( GetStyle( position, textAttrEx ) ) {
    style = textAttrEx;
    return true;
  } else
  { return false; }
}

/// Get the content (uncombined) attributes for this position.
bool wxRichTextParagraphLayoutBox::GetUncombinedStyle( long position, wxTextAttrEx& style ) {
  return DoGetStyle( position, style, false );
}

bool wxRichTextParagraphLayoutBox::GetUncombinedStyle( long position, wxRichTextAttr& style ) {
  wxTextAttrEx textAttrEx( style );
  if( GetUncombinedStyle( position, textAttrEx ) ) {
    style = textAttrEx;
    return true;
  } else
  { return false; }
}

/// Implementation helper for GetStyle. If combineStyles is true, combine base, paragraph and
/// context attributes.
bool wxRichTextParagraphLayoutBox::DoGetStyle( long position, wxTextAttrEx& style, bool combineStyles ) {
  wxRichTextObject* obj wxDUMMY_INITIALIZE( NULL );
  if( style.IsParagraphStyle() ) {
    obj = GetParagraphAtPosition( position );
    if( obj ) {
      if( combineStyles ) {
        // Start with the base style
        style = GetAttributes();
        // Apply the paragraph style
        wxRichTextApplyStyle( style, obj->GetAttributes() );
      } else
      { style = obj->GetAttributes(); }
      return true;
    }
  } else {
    obj = GetLeafObjectAtPosition( position );
    if( obj ) {
      if( combineStyles ) {
        wxRichTextParagraph* para = wxDynamicCast( obj->GetParent(), wxRichTextParagraph );
        style = para ? para->GetCombinedAttributes( obj->GetAttributes() ) : obj->GetAttributes();
      } else
      { style = obj->GetAttributes(); }
      return true;
    }
  }
  return false;
}

static bool wxHasStyle( long flags, long style ) {
  return ( flags & style ) != 0;
}

/// Combines 'style' with 'currentStyle' for the purpose of summarising the attributes of a range of
/// content.
bool wxRichTextParagraphLayoutBox::CollectStyle( wxTextAttrEx& currentStyle, const wxTextAttrEx& style, long& multipleStyleAttributes, int& multipleTextEffectAttributes ) {
  if( style.HasFont() ) {
    if( style.HasFontSize() && !wxHasStyle( multipleStyleAttributes, wxTEXT_ATTR_FONT_SIZE ) ) {
      if( currentStyle.GetFont().Ok() && currentStyle.HasFontSize() ) {
        if( currentStyle.GetFont().GetPointSize() != style.GetFont().GetPointSize() ) {
          // Clash of style - mark as such
          multipleStyleAttributes |= wxTEXT_ATTR_FONT_SIZE;
          currentStyle.SetFlags( currentStyle.GetFlags() & ~wxTEXT_ATTR_FONT_SIZE );
        }
      } else {
        if( !currentStyle.GetFont().Ok() ) {
          wxSetFontPreservingStyles( currentStyle, *wxNORMAL_FONT );
        }
        wxFont font( currentStyle.GetFont() );
        wxFontSetPointSize( font, style.GetFont().GetPointSize() );
        wxSetFontPreservingStyles( currentStyle, font );
        currentStyle.SetFlags( currentStyle.GetFlags() | wxTEXT_ATTR_FONT_SIZE );
      }
    }
    if( style.HasFontItalic() && !wxHasStyle( multipleStyleAttributes, wxTEXT_ATTR_FONT_ITALIC ) ) {
      if( currentStyle.GetFont().Ok() && currentStyle.HasFontItalic() ) {
        if( currentStyle.GetFont().GetStyle() != style.GetFont().GetStyle() ) {
          // Clash of style - mark as such
          multipleStyleAttributes |= wxTEXT_ATTR_FONT_ITALIC;
          currentStyle.SetFlags( currentStyle.GetFlags() & ~wxTEXT_ATTR_FONT_ITALIC );
        }
      } else {
        if( !currentStyle.GetFont().Ok() ) {
          wxSetFontPreservingStyles( currentStyle, *wxNORMAL_FONT );
        }
        wxFont font( currentStyle.GetFont() );
        wxFontSetStyle( font, style.GetFont().GetStyle() );
        wxSetFontPreservingStyles( currentStyle, font );
        currentStyle.SetFlags( currentStyle.GetFlags() | wxTEXT_ATTR_FONT_ITALIC );
      }
    }
    if( style.HasFontWeight() && !wxHasStyle( multipleStyleAttributes, wxTEXT_ATTR_FONT_WEIGHT ) ) {
      if( currentStyle.GetFont().Ok() && currentStyle.HasFontWeight() ) {
        if( currentStyle.GetFont().GetWeight() != style.GetFont().GetWeight() ) {
          // Clash of style - mark as such
          multipleStyleAttributes |= wxTEXT_ATTR_FONT_WEIGHT;
          currentStyle.SetFlags( currentStyle.GetFlags() & ~wxTEXT_ATTR_FONT_WEIGHT );
        }
      } else {
        if( !currentStyle.GetFont().Ok() ) {
          wxSetFontPreservingStyles( currentStyle, *wxNORMAL_FONT );
        }
        wxFont font( currentStyle.GetFont() );
        wxFontSetWeight( font, style.GetFont().GetWeight() );
        wxSetFontPreservingStyles( currentStyle, font );
        currentStyle.SetFlags( currentStyle.GetFlags() | wxTEXT_ATTR_FONT_WEIGHT );
      }
    }
    if( style.HasFontFaceName() && !wxHasStyle( multipleStyleAttributes, wxTEXT_ATTR_FONT_FACE ) ) {
      if( currentStyle.GetFont().Ok() && currentStyle.HasFontFaceName() ) {
        wxString faceName1( currentStyle.GetFont().GetFaceName() );
        wxString faceName2( style.GetFont().GetFaceName() );
        if( faceName1 != faceName2 ) {
          // Clash of style - mark as such
          multipleStyleAttributes |= wxTEXT_ATTR_FONT_FACE;
          currentStyle.SetFlags( currentStyle.GetFlags() & ~wxTEXT_ATTR_FONT_FACE );
        }
      } else {
        if( !currentStyle.GetFont().Ok() ) {
          wxSetFontPreservingStyles( currentStyle, *wxNORMAL_FONT );
        }
        wxFont font( currentStyle.GetFont() );
        wxFontSetFaceName( font, style.GetFont().GetFaceName() );
        wxSetFontPreservingStyles( currentStyle, font );
        currentStyle.SetFlags( currentStyle.GetFlags() | wxTEXT_ATTR_FONT_FACE );
      }
    }
    if( style.HasFontUnderlined() && !wxHasStyle( multipleStyleAttributes, wxTEXT_ATTR_FONT_UNDERLINE ) ) {
      if( currentStyle.GetFont().Ok() && currentStyle.HasFontUnderlined() ) {
        if( currentStyle.GetFont().GetUnderlined() != style.GetFont().GetUnderlined() ) {
          // Clash of style - mark as such
          multipleStyleAttributes |= wxTEXT_ATTR_FONT_UNDERLINE;
          currentStyle.SetFlags( currentStyle.GetFlags() & ~wxTEXT_ATTR_FONT_UNDERLINE );
        }
      } else {
        if( !currentStyle.GetFont().Ok() ) {
          wxSetFontPreservingStyles( currentStyle, *wxNORMAL_FONT );
        }
        wxFont font( currentStyle.GetFont() );
        wxFontSetUnderlined( font, style.GetFont().GetUnderlined() );
        wxSetFontPreservingStyles( currentStyle, font );
        currentStyle.SetFlags( currentStyle.GetFlags() | wxTEXT_ATTR_FONT_UNDERLINE );
      }
    }
  }
  if( style.HasTextColour() && !wxHasStyle( multipleStyleAttributes, wxTEXT_ATTR_TEXT_COLOUR ) ) {
    if( currentStyle.HasTextColour() ) {
      if( currentStyle.GetTextColour() != style.GetTextColour() ) {
        // Clash of style - mark as such
        multipleStyleAttributes |= wxTEXT_ATTR_TEXT_COLOUR;
        currentStyle.SetFlags( currentStyle.GetFlags() & ~wxTEXT_ATTR_TEXT_COLOUR );
      }
    } else
    { currentStyle.SetTextColour( style.GetTextColour() ); }
  }
  if( style.HasBackgroundColour() && !wxHasStyle( multipleStyleAttributes, wxTEXT_ATTR_BACKGROUND_COLOUR ) ) {
    if( currentStyle.HasBackgroundColour() ) {
      if( currentStyle.GetBackgroundColour() != style.GetBackgroundColour() ) {
        // Clash of style - mark as such
        multipleStyleAttributes |= wxTEXT_ATTR_BACKGROUND_COLOUR;
        currentStyle.SetFlags( currentStyle.GetFlags() & ~wxTEXT_ATTR_BACKGROUND_COLOUR );
      }
    } else
    { currentStyle.SetBackgroundColour( style.GetBackgroundColour() ); }
  }
  if( style.HasAlignment() && !wxHasStyle( multipleStyleAttributes, wxTEXT_ATTR_ALIGNMENT ) ) {
    if( currentStyle.HasAlignment() ) {
      if( currentStyle.GetAlignment() != style.GetAlignment() ) {
        // Clash of style - mark as such
        multipleStyleAttributes |= wxTEXT_ATTR_ALIGNMENT;
        currentStyle.SetFlags( currentStyle.GetFlags() & ~wxTEXT_ATTR_ALIGNMENT );
      }
    } else
    { currentStyle.SetAlignment( style.GetAlignment() ); }
  }
  if( style.HasTabs() && !wxHasStyle( multipleStyleAttributes, wxTEXT_ATTR_TABS ) ) {
    if( currentStyle.HasTabs() ) {
      if( !wxRichTextTabsEq( currentStyle.GetTabs(), style.GetTabs() ) ) {
        // Clash of style - mark as such
        multipleStyleAttributes |= wxTEXT_ATTR_TABS;
        currentStyle.SetFlags( currentStyle.GetFlags() & ~wxTEXT_ATTR_TABS );
      }
    } else
    { currentStyle.SetTabs( style.GetTabs() ); }
  }
  if( style.HasLeftIndent() && !wxHasStyle( multipleStyleAttributes, wxTEXT_ATTR_LEFT_INDENT ) ) {
    if( currentStyle.HasLeftIndent() ) {
      if( currentStyle.GetLeftIndent() != style.GetLeftIndent() || currentStyle.GetLeftSubIndent() != style.GetLeftSubIndent() ) {
        // Clash of style - mark as such
        multipleStyleAttributes |= wxTEXT_ATTR_LEFT_INDENT;
        currentStyle.SetFlags( currentStyle.GetFlags() & ~wxTEXT_ATTR_LEFT_INDENT );
      }
    } else
    { currentStyle.SetLeftIndent( style.GetLeftIndent(), style.GetLeftSubIndent() ); }
  }
  if( style.HasRightIndent() && !wxHasStyle( multipleStyleAttributes, wxTEXT_ATTR_RIGHT_INDENT ) ) {
    if( currentStyle.HasRightIndent() ) {
      if( currentStyle.GetRightIndent() != style.GetRightIndent() ) {
        // Clash of style - mark as such
        multipleStyleAttributes |= wxTEXT_ATTR_RIGHT_INDENT;
        currentStyle.SetFlags( currentStyle.GetFlags() & ~wxTEXT_ATTR_RIGHT_INDENT );
      }
    } else
    { currentStyle.SetRightIndent( style.GetRightIndent() ); }
  }
  if( style.HasParagraphSpacingAfter() && !wxHasStyle( multipleStyleAttributes, wxTEXT_ATTR_PARA_SPACING_AFTER ) ) {
    if( currentStyle.HasParagraphSpacingAfter() ) {
      if( currentStyle.GetParagraphSpacingAfter() != style.GetParagraphSpacingAfter() ) {
        // Clash of style - mark as such
        multipleStyleAttributes |= wxTEXT_ATTR_PARA_SPACING_AFTER;
        currentStyle.SetFlags( currentStyle.GetFlags() & ~wxTEXT_ATTR_PARA_SPACING_AFTER );
      }
    } else
    { currentStyle.SetParagraphSpacingAfter( style.GetParagraphSpacingAfter() ); }
  }
  if( style.HasParagraphSpacingBefore() && !wxHasStyle( multipleStyleAttributes, wxTEXT_ATTR_PARA_SPACING_BEFORE ) ) {
    if( currentStyle.HasParagraphSpacingBefore() ) {
      if( currentStyle.GetParagraphSpacingBefore() != style.GetParagraphSpacingBefore() ) {
        // Clash of style - mark as such
        multipleStyleAttributes |= wxTEXT_ATTR_PARA_SPACING_BEFORE;
        currentStyle.SetFlags( currentStyle.GetFlags() & ~wxTEXT_ATTR_PARA_SPACING_BEFORE );
      }
    } else
    { currentStyle.SetParagraphSpacingBefore( style.GetParagraphSpacingBefore() ); }
  }
  if( style.HasLineSpacing() && !wxHasStyle( multipleStyleAttributes, wxTEXT_ATTR_LINE_SPACING ) ) {
    if( currentStyle.HasLineSpacing() ) {
      if( currentStyle.GetLineSpacing() != style.GetLineSpacing() ) {
        // Clash of style - mark as such
        multipleStyleAttributes |= wxTEXT_ATTR_LINE_SPACING;
        currentStyle.SetFlags( currentStyle.GetFlags() & ~wxTEXT_ATTR_LINE_SPACING );
      }
    } else
    { currentStyle.SetLineSpacing( style.GetLineSpacing() ); }
  }
  if( style.HasCharacterStyleName() && !wxHasStyle( multipleStyleAttributes, wxTEXT_ATTR_CHARACTER_STYLE_NAME ) ) {
    if( currentStyle.HasCharacterStyleName() ) {
      if( currentStyle.GetCharacterStyleName() != style.GetCharacterStyleName() ) {
        // Clash of style - mark as such
        multipleStyleAttributes |= wxTEXT_ATTR_CHARACTER_STYLE_NAME;
        currentStyle.SetFlags( currentStyle.GetFlags() & ~wxTEXT_ATTR_CHARACTER_STYLE_NAME );
      }
    } else
    { currentStyle.SetCharacterStyleName( style.GetCharacterStyleName() ); }
  }
  if( style.HasParagraphStyleName() && !wxHasStyle( multipleStyleAttributes, wxTEXT_ATTR_PARAGRAPH_STYLE_NAME ) ) {
    if( currentStyle.HasParagraphStyleName() ) {
      if( currentStyle.GetParagraphStyleName() != style.GetParagraphStyleName() ) {
        // Clash of style - mark as such
        multipleStyleAttributes |= wxTEXT_ATTR_PARAGRAPH_STYLE_NAME;
        currentStyle.SetFlags( currentStyle.GetFlags() & ~wxTEXT_ATTR_PARAGRAPH_STYLE_NAME );
      }
    } else
    { currentStyle.SetParagraphStyleName( style.GetParagraphStyleName() ); }
  }
  if( style.HasListStyleName() && !wxHasStyle( multipleStyleAttributes, wxTEXT_ATTR_LIST_STYLE_NAME ) ) {
    if( currentStyle.HasListStyleName() ) {
      if( currentStyle.GetListStyleName() != style.GetListStyleName() ) {
        // Clash of style - mark as such
        multipleStyleAttributes |= wxTEXT_ATTR_LIST_STYLE_NAME;
        currentStyle.SetFlags( currentStyle.GetFlags() & ~wxTEXT_ATTR_LIST_STYLE_NAME );
      }
    } else
    { currentStyle.SetListStyleName( style.GetListStyleName() ); }
  }
  if( style.HasBulletStyle() && !wxHasStyle( multipleStyleAttributes, wxTEXT_ATTR_BULLET_STYLE ) ) {
    if( currentStyle.HasBulletStyle() ) {
      if( currentStyle.GetBulletStyle() != style.GetBulletStyle() ) {
        // Clash of style - mark as such
        multipleStyleAttributes |= wxTEXT_ATTR_BULLET_STYLE;
        currentStyle.SetFlags( currentStyle.GetFlags() & ~wxTEXT_ATTR_BULLET_STYLE );
      }
    } else
    { currentStyle.SetBulletStyle( style.GetBulletStyle() ); }
  }
  if( style.HasBulletNumber() && !wxHasStyle( multipleStyleAttributes, wxTEXT_ATTR_BULLET_NUMBER ) ) {
    if( currentStyle.HasBulletNumber() ) {
      if( currentStyle.GetBulletNumber() != style.GetBulletNumber() ) {
        // Clash of style - mark as such
        multipleStyleAttributes |= wxTEXT_ATTR_BULLET_NUMBER;
        currentStyle.SetFlags( currentStyle.GetFlags() & ~wxTEXT_ATTR_BULLET_NUMBER );
      }
    } else
    { currentStyle.SetBulletNumber( style.GetBulletNumber() ); }
  }
  if( style.HasBulletText() && !wxHasStyle( multipleStyleAttributes, wxTEXT_ATTR_BULLET_TEXT ) ) {
    if( currentStyle.HasBulletText() ) {
      if( currentStyle.GetBulletText() != style.GetBulletText() ) {
        // Clash of style - mark as such
        multipleStyleAttributes |= wxTEXT_ATTR_BULLET_TEXT;
        currentStyle.SetFlags( currentStyle.GetFlags() & ~wxTEXT_ATTR_BULLET_TEXT );
      }
    } else {
      currentStyle.SetBulletText( style.GetBulletText() );
      currentStyle.SetBulletFont( style.GetBulletFont() );
    }
  }
  if( style.HasBulletName() && !wxHasStyle( multipleStyleAttributes, wxTEXT_ATTR_BULLET_NAME ) ) {
    if( currentStyle.HasBulletName() ) {
      if( currentStyle.GetBulletName() != style.GetBulletName() ) {
        // Clash of style - mark as such
        multipleStyleAttributes |= wxTEXT_ATTR_BULLET_NAME;
        currentStyle.SetFlags( currentStyle.GetFlags() & ~wxTEXT_ATTR_BULLET_NAME );
      }
    } else
    { currentStyle.SetBulletName( style.GetBulletName() ); }
  }
  if( style.HasURL() && !wxHasStyle( multipleStyleAttributes, wxTEXT_ATTR_URL ) ) {
    if( currentStyle.HasURL() ) {
      if( currentStyle.GetURL() != style.GetURL() ) {
        // Clash of style - mark as such
        multipleStyleAttributes |= wxTEXT_ATTR_URL;
        currentStyle.SetFlags( currentStyle.GetFlags() & ~wxTEXT_ATTR_URL );
      }
    } else
    { currentStyle.SetURL( style.GetURL() ); }
  }
  if( style.HasTextEffects() && !wxHasStyle( multipleStyleAttributes, wxTEXT_ATTR_EFFECTS ) ) {
    if( currentStyle.HasTextEffects() ) {
      // We need to find the bits in the new style that are different:
      // just look at those bits that are specified by the new style.
      int currentRelevantTextEffects = currentStyle.GetTextEffects() & style.GetTextEffectFlags();
      int newRelevantTextEffects = style.GetTextEffects() & style.GetTextEffectFlags();
      if( currentRelevantTextEffects != newRelevantTextEffects ) {
        // Find the text effects that were different, using XOR
        int differentEffects = currentRelevantTextEffects ^ newRelevantTextEffects;
        // Clash of style - mark as such
        multipleTextEffectAttributes |= differentEffects;
        currentStyle.SetTextEffectFlags( currentStyle.GetTextEffectFlags() & ~differentEffects );
      }
    } else {
      currentStyle.SetTextEffects( style.GetTextEffects() );
      currentStyle.SetTextEffectFlags( style.GetTextEffectFlags() );
    }
  }
  if( style.HasOutlineLevel() && !wxHasStyle( multipleStyleAttributes, wxTEXT_ATTR_OUTLINE_LEVEL ) ) {
    if( currentStyle.HasOutlineLevel() ) {
      if( currentStyle.GetOutlineLevel() != style.GetOutlineLevel() ) {
        // Clash of style - mark as such
        multipleStyleAttributes |= wxTEXT_ATTR_OUTLINE_LEVEL;
        currentStyle.SetFlags( currentStyle.GetFlags() & ~wxTEXT_ATTR_OUTLINE_LEVEL );
      }
    } else
    { currentStyle.SetOutlineLevel( style.GetOutlineLevel() ); }
  }
  return true;
}

/// Combines 'style' with 'currentStyle' for the purpose of summarising the attributes of a range of
/// content. This version also takes account of attributes *not* being specified, which is just as important
/// as collecting attributes which are both present and common to all examined objects.
bool wxCollectStyle2( wxTextAttrEx& currentStyle, const wxTextAttrEx& style, long& multipleStyleAttributes, int& multipleTextEffectAttributes, long& absentStyleAttributes, int& absentTextEffectAttributes ) {
  absentStyleAttributes |= ( ~style.GetFlags() & wxTEXT_ATTR_ALL );
  absentTextEffectAttributes |= ( ~style.GetTextEffectFlags() & 0xFFFF );
  if( style.HasFont() ) {
    if( style.HasFontSize() && !wxHasStyle( multipleStyleAttributes | absentStyleAttributes, wxTEXT_ATTR_FONT_SIZE ) ) {
      if( currentStyle.GetFont().Ok() && currentStyle.HasFontSize() ) {
        if( currentStyle.GetFont().GetPointSize() != style.GetFont().GetPointSize() ) {
          // Clash of style - mark as such
          multipleStyleAttributes |= wxTEXT_ATTR_FONT_SIZE;
          currentStyle.SetFlags( currentStyle.GetFlags() & ~wxTEXT_ATTR_FONT_SIZE );
        }
      } else {
        if( !currentStyle.GetFont().Ok() ) {
          wxSetFontPreservingStyles( currentStyle, *wxNORMAL_FONT );
        }
        wxFont font( currentStyle.GetFont() );
        wxFontSetPointSize( font, style.GetFont().GetPointSize() );
        wxSetFontPreservingStyles( currentStyle, font );
        currentStyle.SetFlags( currentStyle.GetFlags() | wxTEXT_ATTR_FONT_SIZE );
      }
    }
    if( style.HasFontItalic() && !wxHasStyle( multipleStyleAttributes | absentStyleAttributes, wxTEXT_ATTR_FONT_ITALIC ) ) {
      if( currentStyle.GetFont().Ok() && currentStyle.HasFontItalic() ) {
        if( currentStyle.GetFont().GetStyle() != style.GetFont().GetStyle() ) {
          // Clash of style - mark as such
          multipleStyleAttributes |= wxTEXT_ATTR_FONT_ITALIC;
          currentStyle.SetFlags( currentStyle.GetFlags() & ~wxTEXT_ATTR_FONT_ITALIC );
        }
      } else {
        if( !currentStyle.GetFont().Ok() ) {
          wxSetFontPreservingStyles( currentStyle, *wxNORMAL_FONT );
        }
        wxFont font( currentStyle.GetFont() );
        wxFontSetStyle( font, style.GetFont().GetStyle() );
        wxSetFontPreservingStyles( currentStyle, font );
        currentStyle.SetFlags( currentStyle.GetFlags() | wxTEXT_ATTR_FONT_ITALIC );
      }
    }
    if( style.HasFontWeight() && !wxHasStyle( multipleStyleAttributes | absentStyleAttributes, wxTEXT_ATTR_FONT_WEIGHT ) ) {
      if( currentStyle.GetFont().Ok() && currentStyle.HasFontWeight() ) {
        if( currentStyle.GetFont().GetWeight() != style.GetFont().GetWeight() ) {
          // Clash of style - mark as such
          multipleStyleAttributes |= wxTEXT_ATTR_FONT_WEIGHT;
          currentStyle.SetFlags( currentStyle.GetFlags() & ~wxTEXT_ATTR_FONT_WEIGHT );
        }
      } else {
        if( !currentStyle.GetFont().Ok() ) {
          wxSetFontPreservingStyles( currentStyle, *wxNORMAL_FONT );
        }
        wxFont font( currentStyle.GetFont() );
        wxFontSetWeight( font, style.GetFont().GetWeight() );
        wxSetFontPreservingStyles( currentStyle, font );
        currentStyle.SetFlags( currentStyle.GetFlags() | wxTEXT_ATTR_FONT_WEIGHT );
      }
    }
    if( style.HasFontFaceName() && !wxHasStyle( multipleStyleAttributes | absentStyleAttributes, wxTEXT_ATTR_FONT_FACE ) ) {
      if( currentStyle.GetFont().Ok() && currentStyle.HasFontFaceName() ) {
        wxString faceName1( currentStyle.GetFont().GetFaceName() );
        wxString faceName2( style.GetFont().GetFaceName() );
        if( faceName1 != faceName2 ) {
          // Clash of style - mark as such
          multipleStyleAttributes |= wxTEXT_ATTR_FONT_FACE;
          currentStyle.SetFlags( currentStyle.GetFlags() & ~wxTEXT_ATTR_FONT_FACE );
        }
      } else {
        if( !currentStyle.GetFont().Ok() ) {
          wxSetFontPreservingStyles( currentStyle, *wxNORMAL_FONT );
        }
        wxFont font( currentStyle.GetFont() );
        wxFontSetFaceName( font, style.GetFont().GetFaceName() );
        wxSetFontPreservingStyles( currentStyle, font );
        currentStyle.SetFlags( currentStyle.GetFlags() | wxTEXT_ATTR_FONT_FACE );
      }
    }
    if( style.HasFontUnderlined() && !wxHasStyle( multipleStyleAttributes | absentStyleAttributes, wxTEXT_ATTR_FONT_UNDERLINE ) ) {
      if( currentStyle.GetFont().Ok() && currentStyle.HasFontUnderlined() ) {
        if( currentStyle.GetFont().GetUnderlined() != style.GetFont().GetUnderlined() ) {
          // Clash of style - mark as such
          multipleStyleAttributes |= wxTEXT_ATTR_FONT_UNDERLINE;
          currentStyle.SetFlags( currentStyle.GetFlags() & ~wxTEXT_ATTR_FONT_UNDERLINE );
        }
      } else {
        if( !currentStyle.GetFont().Ok() ) {
          wxSetFontPreservingStyles( currentStyle, *wxNORMAL_FONT );
        }
        wxFont font( currentStyle.GetFont() );
        wxFontSetUnderlined( font, style.GetFont().GetUnderlined() );
        wxSetFontPreservingStyles( currentStyle, font );
        currentStyle.SetFlags( currentStyle.GetFlags() | wxTEXT_ATTR_FONT_UNDERLINE );
      }
    }
  }
  if( style.HasTextColour() && !wxHasStyle( multipleStyleAttributes | absentStyleAttributes, wxTEXT_ATTR_TEXT_COLOUR ) ) {
    if( currentStyle.HasTextColour() ) {
      if( currentStyle.GetTextColour() != style.GetTextColour() ) {
        // Clash of style - mark as such
        multipleStyleAttributes |= wxTEXT_ATTR_TEXT_COLOUR;
        currentStyle.SetFlags( currentStyle.GetFlags() & ~wxTEXT_ATTR_TEXT_COLOUR );
      }
    } else
    { currentStyle.SetTextColour( style.GetTextColour() ); }
  }
  if( style.HasBackgroundColour() && !wxHasStyle( multipleStyleAttributes | absentStyleAttributes, wxTEXT_ATTR_BACKGROUND_COLOUR ) ) {
    if( currentStyle.HasBackgroundColour() ) {
      if( currentStyle.GetBackgroundColour() != style.GetBackgroundColour() ) {
        // Clash of style - mark as such
        multipleStyleAttributes |= wxTEXT_ATTR_BACKGROUND_COLOUR;
        currentStyle.SetFlags( currentStyle.GetFlags() & ~wxTEXT_ATTR_BACKGROUND_COLOUR );
      }
    } else
    { currentStyle.SetBackgroundColour( style.GetBackgroundColour() ); }
  }
  if( style.HasAlignment() && !wxHasStyle( multipleStyleAttributes | absentStyleAttributes, wxTEXT_ATTR_ALIGNMENT ) ) {
    if( currentStyle.HasAlignment() ) {
      if( currentStyle.GetAlignment() != style.GetAlignment() ) {
        // Clash of style - mark as such
        multipleStyleAttributes |= wxTEXT_ATTR_ALIGNMENT;
        currentStyle.SetFlags( currentStyle.GetFlags() & ~wxTEXT_ATTR_ALIGNMENT );
      }
    } else
    { currentStyle.SetAlignment( style.GetAlignment() ); }
  }
  if( style.HasTabs() && !wxHasStyle( multipleStyleAttributes | absentStyleAttributes, wxTEXT_ATTR_TABS ) ) {
    if( currentStyle.HasTabs() ) {
      if( !wxRichTextTabsEq( currentStyle.GetTabs(), style.GetTabs() ) ) {
        // Clash of style - mark as such
        multipleStyleAttributes |= wxTEXT_ATTR_TABS;
        currentStyle.SetFlags( currentStyle.GetFlags() & ~wxTEXT_ATTR_TABS );
      }
    } else
    { currentStyle.SetTabs( style.GetTabs() ); }
  }
  if( style.HasLeftIndent() && !wxHasStyle( multipleStyleAttributes | absentStyleAttributes, wxTEXT_ATTR_LEFT_INDENT ) ) {
    if( currentStyle.HasLeftIndent() ) {
      if( currentStyle.GetLeftIndent() != style.GetLeftIndent() || currentStyle.GetLeftSubIndent() != style.GetLeftSubIndent() ) {
        // Clash of style - mark as such
        multipleStyleAttributes |= wxTEXT_ATTR_LEFT_INDENT;
        currentStyle.SetFlags( currentStyle.GetFlags() & ~wxTEXT_ATTR_LEFT_INDENT );
      }
    } else
    { currentStyle.SetLeftIndent( style.GetLeftIndent(), style.GetLeftSubIndent() ); }
  }
  if( style.HasRightIndent() && !wxHasStyle( multipleStyleAttributes | absentStyleAttributes, wxTEXT_ATTR_RIGHT_INDENT ) ) {
    if( currentStyle.HasRightIndent() ) {
      if( currentStyle.GetRightIndent() != style.GetRightIndent() ) {
        // Clash of style - mark as such
        multipleStyleAttributes |= wxTEXT_ATTR_RIGHT_INDENT;
        currentStyle.SetFlags( currentStyle.GetFlags() & ~wxTEXT_ATTR_RIGHT_INDENT );
      }
    } else
    { currentStyle.SetRightIndent( style.GetRightIndent() ); }
  }
  if( style.HasParagraphSpacingAfter() && !wxHasStyle( multipleStyleAttributes | absentStyleAttributes, wxTEXT_ATTR_PARA_SPACING_AFTER ) ) {
    if( currentStyle.HasParagraphSpacingAfter() ) {
      if( currentStyle.GetParagraphSpacingAfter() != style.GetParagraphSpacingAfter() ) {
        // Clash of style - mark as such
        multipleStyleAttributes |= wxTEXT_ATTR_PARA_SPACING_AFTER;
        currentStyle.SetFlags( currentStyle.GetFlags() & ~wxTEXT_ATTR_PARA_SPACING_AFTER );
      }
    } else
    { currentStyle.SetParagraphSpacingAfter( style.GetParagraphSpacingAfter() ); }
  }
  if( style.HasParagraphSpacingBefore() && !wxHasStyle( multipleStyleAttributes | absentStyleAttributes, wxTEXT_ATTR_PARA_SPACING_BEFORE ) ) {
    if( currentStyle.HasParagraphSpacingBefore() ) {
      if( currentStyle.GetParagraphSpacingBefore() != style.GetParagraphSpacingBefore() ) {
        // Clash of style - mark as such
        multipleStyleAttributes |= wxTEXT_ATTR_PARA_SPACING_BEFORE;
        currentStyle.SetFlags( currentStyle.GetFlags() & ~wxTEXT_ATTR_PARA_SPACING_BEFORE );
      }
    } else
    { currentStyle.SetParagraphSpacingBefore( style.GetParagraphSpacingBefore() ); }
  }
  if( style.HasLineSpacing() && !wxHasStyle( multipleStyleAttributes | absentStyleAttributes, wxTEXT_ATTR_LINE_SPACING ) ) {
    if( currentStyle.HasLineSpacing() ) {
      if( currentStyle.GetLineSpacing() != style.GetLineSpacing() ) {
        // Clash of style - mark as such
        multipleStyleAttributes |= wxTEXT_ATTR_LINE_SPACING;
        currentStyle.SetFlags( currentStyle.GetFlags() & ~wxTEXT_ATTR_LINE_SPACING );
      }
    } else
    { currentStyle.SetLineSpacing( style.GetLineSpacing() ); }
  }
  if( style.HasCharacterStyleName() && !wxHasStyle( multipleStyleAttributes | absentStyleAttributes, wxTEXT_ATTR_CHARACTER_STYLE_NAME ) ) {
    if( currentStyle.HasCharacterStyleName() ) {
      if( currentStyle.GetCharacterStyleName() != style.GetCharacterStyleName() ) {
        // Clash of style - mark as such
        multipleStyleAttributes |= wxTEXT_ATTR_CHARACTER_STYLE_NAME;
        currentStyle.SetFlags( currentStyle.GetFlags() & ~wxTEXT_ATTR_CHARACTER_STYLE_NAME );
      }
    } else
    { currentStyle.SetCharacterStyleName( style.GetCharacterStyleName() ); }
  }
  if( style.HasParagraphStyleName() && !wxHasStyle( multipleStyleAttributes | absentStyleAttributes, wxTEXT_ATTR_PARAGRAPH_STYLE_NAME ) ) {
    if( currentStyle.HasParagraphStyleName() ) {
      if( currentStyle.GetParagraphStyleName() != style.GetParagraphStyleName() ) {
        // Clash of style - mark as such
        multipleStyleAttributes |= wxTEXT_ATTR_PARAGRAPH_STYLE_NAME;
        currentStyle.SetFlags( currentStyle.GetFlags() & ~wxTEXT_ATTR_PARAGRAPH_STYLE_NAME );
      }
    } else
    { currentStyle.SetParagraphStyleName( style.GetParagraphStyleName() ); }
  }
  if( style.HasListStyleName() && !wxHasStyle( multipleStyleAttributes | absentStyleAttributes, wxTEXT_ATTR_LIST_STYLE_NAME ) ) {
    if( currentStyle.HasListStyleName() ) {
      if( currentStyle.GetListStyleName() != style.GetListStyleName() ) {
        // Clash of style - mark as such
        multipleStyleAttributes |= wxTEXT_ATTR_LIST_STYLE_NAME;
        currentStyle.SetFlags( currentStyle.GetFlags() & ~wxTEXT_ATTR_LIST_STYLE_NAME );
      }
    } else
    { currentStyle.SetListStyleName( style.GetListStyleName() ); }
  }
  if( style.HasBulletStyle() && !wxHasStyle( multipleStyleAttributes | absentStyleAttributes, wxTEXT_ATTR_BULLET_STYLE ) ) {
    if( currentStyle.HasBulletStyle() ) {
      if( currentStyle.GetBulletStyle() != style.GetBulletStyle() ) {
        // Clash of style - mark as such
        multipleStyleAttributes |= wxTEXT_ATTR_BULLET_STYLE;
        currentStyle.SetFlags( currentStyle.GetFlags() & ~wxTEXT_ATTR_BULLET_STYLE );
      }
    } else
    { currentStyle.SetBulletStyle( style.GetBulletStyle() ); }
  }
  if( style.HasBulletNumber() && !wxHasStyle( multipleStyleAttributes | absentStyleAttributes, wxTEXT_ATTR_BULLET_NUMBER ) ) {
    if( currentStyle.HasBulletNumber() ) {
      if( currentStyle.GetBulletNumber() != style.GetBulletNumber() ) {
        // Clash of style - mark as such
        multipleStyleAttributes |= wxTEXT_ATTR_BULLET_NUMBER;
        currentStyle.SetFlags( currentStyle.GetFlags() & ~wxTEXT_ATTR_BULLET_NUMBER );
      }
    } else
    { currentStyle.SetBulletNumber( style.GetBulletNumber() ); }
  }
  if( style.HasBulletText() && !wxHasStyle( multipleStyleAttributes | absentStyleAttributes, wxTEXT_ATTR_BULLET_TEXT ) ) {
    if( currentStyle.HasBulletText() ) {
      if( currentStyle.GetBulletText() != style.GetBulletText() ) {
        // Clash of style - mark as such
        multipleStyleAttributes |= wxTEXT_ATTR_BULLET_TEXT;
        currentStyle.SetFlags( currentStyle.GetFlags() & ~wxTEXT_ATTR_BULLET_TEXT );
      }
    } else {
      currentStyle.SetBulletText( style.GetBulletText() );
      currentStyle.SetBulletFont( style.GetBulletFont() );
    }
  }
  if( style.HasBulletName() && !wxHasStyle( multipleStyleAttributes | absentStyleAttributes, wxTEXT_ATTR_BULLET_NAME ) ) {
    if( currentStyle.HasBulletName() ) {
      if( currentStyle.GetBulletName() != style.GetBulletName() ) {
        // Clash of style - mark as such
        multipleStyleAttributes |= wxTEXT_ATTR_BULLET_NAME;
        currentStyle.SetFlags( currentStyle.GetFlags() & ~wxTEXT_ATTR_BULLET_NAME );
      }
    } else
    { currentStyle.SetBulletName( style.GetBulletName() ); }
  }
  if( style.HasURL() && !wxHasStyle( multipleStyleAttributes | absentStyleAttributes, wxTEXT_ATTR_URL ) ) {
    if( currentStyle.HasURL() ) {
      if( currentStyle.GetURL() != style.GetURL() ) {
        // Clash of style - mark as such
        multipleStyleAttributes |= wxTEXT_ATTR_URL;
        currentStyle.SetFlags( currentStyle.GetFlags() & ~wxTEXT_ATTR_URL );
      }
    } else
    { currentStyle.SetURL( style.GetURL() ); }
  }
  if( style.HasTextEffects() && !wxHasStyle( multipleStyleAttributes | absentStyleAttributes, wxTEXT_ATTR_EFFECTS ) ) {
    if( currentStyle.HasTextEffects() ) {
      // We need to find the bits in the new style that are different:
      // just look at those bits that are specified by the new style.
      // We need to remove the bits and flags that are not common between current style
      // and new style. In so doing we need to take account of the styles absent from one or more of the
      // previous styles.
      int currentRelevantTextEffects = currentStyle.GetTextEffects() & style.GetTextEffectFlags();
      int newRelevantTextEffects = style.GetTextEffects() & style.GetTextEffectFlags();
      if( currentRelevantTextEffects != newRelevantTextEffects ) {
        // Find the text effects that were different, using XOR
        int differentEffects = currentRelevantTextEffects ^ newRelevantTextEffects;
        // Clash of style - mark as such
        multipleTextEffectAttributes |= differentEffects;
        currentStyle.SetTextEffectFlags( currentStyle.GetTextEffectFlags() & ~differentEffects );
      }
    } else {
      currentStyle.SetTextEffects( style.GetTextEffects() );
      currentStyle.SetTextEffectFlags( style.GetTextEffectFlags() );
    }
    // Mask out the flags and values that cannot be common because they were absent in one or more objecrs
    // that we've looked at so far
    currentStyle.SetTextEffects( currentStyle.GetTextEffects() & ~absentTextEffectAttributes );
    currentStyle.SetTextEffectFlags( currentStyle.GetTextEffectFlags() & ~absentTextEffectAttributes );
    if( currentStyle.GetTextEffectFlags() == 0 ) {
      currentStyle.SetFlags( currentStyle.GetFlags() & ~wxTEXT_ATTR_EFFECTS );
    }
  }
  if( style.HasOutlineLevel() && !wxHasStyle( multipleStyleAttributes | absentStyleAttributes, wxTEXT_ATTR_OUTLINE_LEVEL ) ) {
    if( currentStyle.HasOutlineLevel() ) {
      if( currentStyle.GetOutlineLevel() != style.GetOutlineLevel() ) {
        // Clash of style - mark as such
        multipleStyleAttributes |= wxTEXT_ATTR_OUTLINE_LEVEL;
        currentStyle.SetFlags( currentStyle.GetFlags() & ~wxTEXT_ATTR_OUTLINE_LEVEL );
      }
    } else
    { currentStyle.SetOutlineLevel( style.GetOutlineLevel() ); }
  }
  return true;
}

/// Get the combined style for a range - if any attribute is different within the range,
/// that attribute is not present within the flags.
/// *** Note that this is not recursive, and so assumes that content inside a paragraph is not itself
/// nested.
bool wxRichTextParagraphLayoutBox::GetStyleForRange( const wxRichTextRange& range, wxTextAttrEx& style ) {
  style = wxTextAttrEx();
  // The attributes that aren't valid because of multiple styles within the range
  long multipleStyleAttributes = 0;
  int multipleTextEffectAttributes = 0;
  long absentStyleAttributesPara = 0;
  long absentStyleAttributesChar = 0;
  int absentTextEffectAttributesPara = 0;
  int absentTextEffectAttributesChar = 0;
  wxRichTextObjectList::compatibility_iterator node = GetChildren().GetFirst();
  while( node ) {
    wxRichTextParagraph* para = ( wxRichTextParagraph* ) node->GetData();
    if( !( para->GetRange().GetStart() > range.GetEnd() || para->GetRange().GetEnd() < range.GetStart() ) ) {
      if( para->GetChildren().GetCount() == 0 ) {
        wxTextAttrEx paraStyle = para->GetCombinedAttributes();
        wxCollectStyle2( style, paraStyle, multipleStyleAttributes, multipleTextEffectAttributes, absentStyleAttributesPara, absentTextEffectAttributesPara );
      } else {
        wxRichTextRange paraRange( para->GetRange() );
        paraRange.LimitTo( range );
        // First collect paragraph attributes only
        wxTextAttrEx paraStyle = para->GetCombinedAttributes();
        paraStyle.SetFlags( paraStyle.GetFlags() & wxTEXT_ATTR_PARAGRAPH );
        wxCollectStyle2( style, paraStyle, multipleStyleAttributes, multipleTextEffectAttributes, absentStyleAttributesPara, absentTextEffectAttributesPara );
        wxRichTextObjectList::compatibility_iterator childNode = para->GetChildren().GetFirst();
        while( childNode ) {
          wxRichTextObject* child = childNode->GetData();
          if( !( child->GetRange().GetStart() > range.GetEnd() || child->GetRange().GetEnd() < range.GetStart() ) ) {
            wxTextAttrEx childStyle = para->GetCombinedAttributes( child->GetAttributes() );
            // Now collect character attributes only
            childStyle.SetFlags( childStyle.GetFlags() & wxTEXT_ATTR_CHARACTER );
            wxCollectStyle2( style, childStyle, multipleStyleAttributes, multipleTextEffectAttributes, absentStyleAttributesChar, absentTextEffectAttributesChar );
          }
          childNode = childNode->GetNext();
        }
      }
    }
    node = node->GetNext();
  }
  return true;
}

/// Set default style
bool wxRichTextParagraphLayoutBox::SetDefaultStyle( const wxTextAttrEx& style ) {
  m_defaultAttributes = style;
  return true;
}

/// Test if this whole range has character attributes of the specified kind. If any
/// of the attributes are different within the range, the test fails. You
/// can use this to implement, for example, bold button updating. style must have
/// flags indicating which attributes are of interest.
bool wxRichTextParagraphLayoutBox::HasCharacterAttributes( const wxRichTextRange& range, const wxRichTextAttr& style ) const {
  int foundCount = 0;
  int matchingCount = 0;
  wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
  while( node ) {
    wxRichTextParagraph* para = wxDynamicCast( node->GetData(), wxRichTextParagraph );
    wxASSERT( para != NULL );
    if( para ) {
      // Stop searching if we're beyond the range of interest
      if( para->GetRange().GetStart() > range.GetEnd() ) {
        return foundCount == matchingCount && foundCount != 0;
      }
      if( !para->GetRange().IsOutside( range ) ) {
        wxRichTextObjectList::compatibility_iterator node2 = para->GetChildren().GetFirst();
        while( node2 ) {
          wxRichTextObject* child = node2->GetData();
          // Allow for empty string if no buffer
          wxRichTextRange childRange = child->GetRange();
          if( childRange.GetLength() == 0 && GetRange().GetLength() == 1 ) {
            childRange.SetEnd( childRange.GetEnd() + 1 );
          }
          if( !childRange.IsOutside( range ) && child->IsKindOf( CLASSINFO( wxRichTextPlainText ) ) ) {
            foundCount ++;
            wxTextAttrEx textAttr = para->GetCombinedAttributes( child->GetAttributes() );
            if( wxTextAttrEqPartial( textAttr, style, style.GetFlags() ) )
            { matchingCount ++; }
          }
          node2 = node2->GetNext();
        }
      }
    }
    node = node->GetNext();
  }
  return foundCount == matchingCount && foundCount != 0;
}

bool wxRichTextParagraphLayoutBox::HasCharacterAttributes( const wxRichTextRange& range, const wxTextAttrEx& style ) const {
  wxRichTextAttr richStyle = style;
  return HasCharacterAttributes( range, richStyle );
}

/// Test if this whole range has paragraph attributes of the specified kind. If any
/// of the attributes are different within the range, the test fails. You
/// can use this to implement, for example, centering button updating. style must have
/// flags indicating which attributes are of interest.
bool wxRichTextParagraphLayoutBox::HasParagraphAttributes( const wxRichTextRange& range, const wxRichTextAttr& style ) const {
  int foundCount = 0;
  int matchingCount = 0;
  wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
  while( node ) {
    wxRichTextParagraph* para = wxDynamicCast( node->GetData(), wxRichTextParagraph );
    wxASSERT( para != NULL );
    if( para ) {
      // Stop searching if we're beyond the range of interest
      if( para->GetRange().GetStart() > range.GetEnd() ) {
        return foundCount == matchingCount && foundCount != 0;
      }
      if( !para->GetRange().IsOutside( range ) ) {
        wxTextAttrEx textAttr = GetAttributes();
        // Apply the paragraph style
        wxRichTextApplyStyle( textAttr, para->GetAttributes() );
        foundCount ++;
        if( wxTextAttrEqPartial( textAttr, style, style.GetFlags() ) ) {
          matchingCount ++;
        }
      }
    }
    node = node->GetNext();
  }
  return foundCount == matchingCount && foundCount != 0;
}

bool wxRichTextParagraphLayoutBox::HasParagraphAttributes( const wxRichTextRange& range, const wxTextAttrEx& style ) const {
  wxRichTextAttr richStyle = style;
  return HasParagraphAttributes( range, richStyle );
}

void wxRichTextParagraphLayoutBox::Clear() {
  DeleteChildren();
}

void wxRichTextParagraphLayoutBox::Reset() {
  Clear();
  wxRichTextBuffer* buffer = wxDynamicCast( this, wxRichTextBuffer );
  if( buffer && GetRichTextCtrl() ) {
    wxRichTextEvent event( wxEVT_COMMAND_RICHTEXT_BUFFER_RESET, GetRichTextCtrl()->GetId() );
    event.SetEventObject( GetRichTextCtrl() );
    buffer->SendEvent( event, true );
  }
  AddParagraph( wxEmptyString );
  Invalidate( wxRICHTEXT_ALL );
}

/// Invalidate the buffer. With no argument, invalidates whole buffer.
void wxRichTextParagraphLayoutBox::Invalidate( const wxRichTextRange& invalidRange ) {
  SetDirty( true );
  if( invalidRange == wxRICHTEXT_ALL ) {
    m_invalidRange = wxRICHTEXT_ALL;
    return;
  }
  // Already invalidating everything
  if( m_invalidRange == wxRICHTEXT_ALL ) {
    return;
  }
  if( ( invalidRange.GetStart() < m_invalidRange.GetStart() ) || m_invalidRange.GetStart() == -1 ) {
    m_invalidRange.SetStart( invalidRange.GetStart() );
  }
  if( invalidRange.GetEnd() > m_invalidRange.GetEnd() ) {
    m_invalidRange.SetEnd( invalidRange.GetEnd() );
  }
}

/// Get invalid range, rounding to entire paragraphs if argument is true.
wxRichTextRange wxRichTextParagraphLayoutBox::GetInvalidRange( bool wholeParagraphs ) const {
  if( m_invalidRange == wxRICHTEXT_ALL || m_invalidRange == wxRICHTEXT_NONE ) {
    return m_invalidRange;
  }
  wxRichTextRange range = m_invalidRange;
  if( wholeParagraphs ) {
    wxRichTextParagraph* para1 = GetParagraphAtPosition( range.GetStart() );
    wxRichTextParagraph* para2 = GetParagraphAtPosition( range.GetEnd() );
    if( para1 ) {
      range.SetStart( para1->GetRange().GetStart() );
    }
    if( para2 ) {
      range.SetEnd( para2->GetRange().GetEnd() );
    }
  }
  return range;
}

/// Apply the style sheet to the buffer, for example if the styles have changed.
bool wxRichTextParagraphLayoutBox::ApplyStyleSheet( wxRichTextStyleSheet* styleSheet ) {
  wxASSERT( styleSheet != NULL );
  if( !styleSheet ) {
    return false;
  }
  int foundCount = 0;
  wxRichTextAttr attr( GetBasicStyle() );
  if( GetBasicStyle().HasParagraphStyleName() ) {
    wxRichTextParagraphStyleDefinition* paraDef = styleSheet->FindParagraphStyle( GetBasicStyle().GetParagraphStyleName() );
    if( paraDef ) {
      attr.Apply( paraDef->GetStyleMergedWithBase( styleSheet ) );
      SetBasicStyle( attr );
      foundCount ++;
    }
  }
  if( GetBasicStyle().HasCharacterStyleName() ) {
    wxRichTextCharacterStyleDefinition* charDef = styleSheet->FindCharacterStyle( GetBasicStyle().GetCharacterStyleName() );
    if( charDef ) {
      attr.Apply( charDef->GetStyleMergedWithBase( styleSheet ) );
      SetBasicStyle( attr );
      foundCount ++;
    }
  }
  wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
  while( node ) {
    wxRichTextParagraph* para = wxDynamicCast( node->GetData(), wxRichTextParagraph );
    wxASSERT( para != NULL );
    if( para ) {
      // Combine paragraph and list styles. If there is a list style in the original attributes,
      // the current indentation overrides anything else and is used to find the item indentation.
      // Also, for applying paragraph styles, consider having 2 modes: (1) we merge with what we have,
      // thereby taking into account all user changes, (2) reset the style completely (except for indentation/list
      // exception as above).
      // Problem: when changing from one list style to another, there's a danger that the level info will get lost.
      // So when changing a list style interactively, could retrieve level based on current style, then
      // set appropriate indent and apply new style.
      int outline = -1;
      int num = -1;
      if( para->GetAttributes().HasOutlineLevel() ) {
        outline = para->GetAttributes().GetOutlineLevel();
      }
      if( para->GetAttributes().HasBulletNumber() ) {
        num = para->GetAttributes().GetBulletNumber();
      }
      if( !para->GetAttributes().GetParagraphStyleName().IsEmpty() && !para->GetAttributes().GetListStyleName().IsEmpty() ) {
        int currentIndent = para->GetAttributes().GetLeftIndent();
        wxRichTextParagraphStyleDefinition* paraDef = styleSheet->FindParagraphStyle( para->GetAttributes().GetParagraphStyleName() );
        wxRichTextListStyleDefinition* listDef = styleSheet->FindListStyle( para->GetAttributes().GetListStyleName() );
        if( paraDef && !listDef ) {
          para->GetAttributes() = paraDef->GetStyleMergedWithBase( styleSheet );
          foundCount ++;
        } else if( listDef && !paraDef ) {
          // Set overall style defined for the list style definition
          para->GetAttributes() = listDef->GetStyleMergedWithBase( styleSheet );
          // Apply the style for this level
          wxRichTextApplyStyle( para->GetAttributes(), * listDef->GetLevelAttributes( listDef->FindLevelForIndent( currentIndent ) ) );
          foundCount ++;
        } else if( listDef && paraDef ) {
          // Combines overall list style, style for level, and paragraph style
          para->GetAttributes() = listDef->CombineWithParagraphStyle( currentIndent, paraDef->GetStyleMergedWithBase( styleSheet ) );
          foundCount ++;
        }
      } else if( para->GetAttributes().GetParagraphStyleName().IsEmpty() && !para->GetAttributes().GetListStyleName().IsEmpty() ) {
        int currentIndent = para->GetAttributes().GetLeftIndent();
        wxRichTextListStyleDefinition* listDef = styleSheet->FindListStyle( para->GetAttributes().GetListStyleName() );
        // Overall list definition style
        para->GetAttributes() = listDef->GetStyleMergedWithBase( styleSheet );
        // Style for this level
        wxRichTextApplyStyle( para->GetAttributes(), * listDef->GetLevelAttributes( listDef->FindLevelForIndent( currentIndent ) ) );
        foundCount ++;
      } else if( !para->GetAttributes().GetParagraphStyleName().IsEmpty() && para->GetAttributes().GetListStyleName().IsEmpty() ) {
        wxRichTextParagraphStyleDefinition* def = styleSheet->FindParagraphStyle( para->GetAttributes().GetParagraphStyleName() );
        if( def ) {
          para->GetAttributes() = def->GetStyleMergedWithBase( styleSheet );
          foundCount ++;
        }
      }
      if( outline != -1 ) {
        para->GetAttributes().SetOutlineLevel( outline );
      }
      if( num != -1 ) {
        para->GetAttributes().SetBulletNumber( num );
      }
    }
    node = node->GetNext();
  }
  return foundCount != 0;
}

/// Set list style
bool wxRichTextParagraphLayoutBox::SetListStyle( const wxRichTextRange& range, wxRichTextListStyleDefinition* def, int flags, int startFrom, int specifiedLevel ) {
  wxRichTextStyleSheet* styleSheet = GetStyleSheet();
  bool withUndo = ( ( flags & wxRICHTEXT_SETSTYLE_WITH_UNDO ) != 0 );
  // bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
  bool specifyLevel = ( ( flags & wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL ) != 0 );
  bool renumber = ( ( flags & wxRICHTEXT_SETSTYLE_RENUMBER ) != 0 );
  // Current number, if numbering
  int n = startFrom;
  wxASSERT( !specifyLevel || ( specifyLevel && ( specifiedLevel >= 0 ) ) );
  // If we are associated with a control, make undoable; otherwise, apply immediately
  // to the data.
  bool haveControl = ( GetRichTextCtrl() != NULL );
  wxRichTextAction* action = NULL;
  if( haveControl && withUndo ) {
    action = new wxRichTextAction( NULL, _( "Change List Style" ), wxRICHTEXT_CHANGE_STYLE, & GetRichTextCtrl()->GetBuffer(), GetRichTextCtrl() );
    action->SetRange( range );
    action->SetPosition( GetRichTextCtrl()->GetCaretPosition() );
  }
  wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
  while( node ) {
    wxRichTextParagraph* para = wxDynamicCast( node->GetData(), wxRichTextParagraph );
    wxASSERT( para != NULL );
    if( para && para->GetChildCount() > 0 ) {
      // Stop searching if we're beyond the range of interest
      if( para->GetRange().GetStart() > range.GetEnd() ) {
        break;
      }
      if( !para->GetRange().IsOutside( range ) ) {
        // We'll be using a copy of the paragraph to make style changes,
        // not updating the buffer directly.
        wxRichTextParagraph* newPara wxDUMMY_INITIALIZE( NULL );
        if( haveControl && withUndo ) {
          newPara = new wxRichTextParagraph( *para );
          action->GetNewParagraphs().AppendChild( newPara );
          // Also store the old ones for Undo
          action->GetOldParagraphs().AppendChild( new wxRichTextParagraph( *para ) );
        } else
        { newPara = para; }
        if( def ) {
          int thisIndent = newPara->GetAttributes().GetLeftIndent();
          int thisLevel = specifyLevel ? specifiedLevel : def->FindLevelForIndent( thisIndent );
          // How is numbering going to work?
          // If we are renumbering, or numbering for the first time, we need to keep
          // track of the number for each level. But we might be simply applying a different
          // list style.
          // In Word, applying a style to several paragraphs, even if at different levels,
          // reverts the level back to the same one. So we could do the same here.
          // Renumbering will need to be done when we promote/demote a paragraph.
          // Apply the overall list style, and item style for this level
          wxTextAttrEx listStyle( def->GetCombinedStyleForLevel( thisLevel, styleSheet ) );
          wxRichTextApplyStyle( newPara->GetAttributes(), listStyle );
          // Now we need to do numbering
          if( renumber ) {
            newPara->GetAttributes().SetBulletNumber( n );
          }
          n ++;
        } else if( !newPara->GetAttributes().GetListStyleName().IsEmpty() ) {
          // if def is NULL, remove list style, applying any associated paragraph style
          // to restore the attributes
          newPara->GetAttributes().SetListStyleName( wxEmptyString );
          newPara->GetAttributes().SetLeftIndent( 0, 0 );
          newPara->GetAttributes().SetBulletText( wxEmptyString );
          // Eliminate the main list-related attributes
          newPara->GetAttributes().SetFlags( newPara->GetAttributes().GetFlags() & ~wxTEXT_ATTR_LEFT_INDENT & ~wxTEXT_ATTR_BULLET_STYLE & ~wxTEXT_ATTR_BULLET_NUMBER & ~wxTEXT_ATTR_BULLET_TEXT & wxTEXT_ATTR_LIST_STYLE_NAME );
          if( styleSheet && !newPara->GetAttributes().GetParagraphStyleName().IsEmpty() ) {
            wxRichTextParagraphStyleDefinition* def = styleSheet->FindParagraphStyle( newPara->GetAttributes().GetParagraphStyleName() );
            if( def )
            { newPara->GetAttributes() = def->GetStyleMergedWithBase( styleSheet ); }
          }
        }
      }
    }
    node = node->GetNext();
  }
  // Do action, or delay it until end of batch.
  if( haveControl && withUndo ) {
    GetRichTextCtrl()->GetBuffer().SubmitAction( action );
  }
  return true;
}

bool wxRichTextParagraphLayoutBox::SetListStyle( const wxRichTextRange& range, const wxString& defName, int flags, int startFrom, int specifiedLevel ) {
  if( GetStyleSheet() ) {
    wxRichTextListStyleDefinition* def = GetStyleSheet()->FindListStyle( defName );
    if( def ) {
      return SetListStyle( range, def, flags, startFrom, specifiedLevel );
    }
  }
  return false;
}

/// Clear list for given range
bool wxRichTextParagraphLayoutBox::ClearListStyle( const wxRichTextRange& range, int flags ) {
  return SetListStyle( range, NULL, flags );
}

/// Number/renumber any list elements in the given range
bool wxRichTextParagraphLayoutBox::NumberList( const wxRichTextRange& range, wxRichTextListStyleDefinition* def, int flags, int startFrom, int specifiedLevel ) {
  return DoNumberList( range, range, 0, def, flags, startFrom, specifiedLevel );
}

/// Number/renumber any list elements in the given range. Also do promotion or demotion of items, if specified
bool wxRichTextParagraphLayoutBox::DoNumberList( const wxRichTextRange& range, const wxRichTextRange& promotionRange, int promoteBy,
    wxRichTextListStyleDefinition* def, int flags, int startFrom, int specifiedLevel ) {
  wxRichTextStyleSheet* styleSheet = GetStyleSheet();
  bool withUndo = ( ( flags & wxRICHTEXT_SETSTYLE_WITH_UNDO ) != 0 );
  // bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
  #ifdef __WXDEBUG__
  bool specifyLevel = ( ( flags & wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL ) != 0 );
  #endif
  bool renumber = ( ( flags & wxRICHTEXT_SETSTYLE_RENUMBER ) != 0 );
  // Max number of levels
  const int maxLevels = 10;
  // The level we're looking at now
  int currentLevel = -1;
  // The item number for each level
  int levels[maxLevels];
  int i;
  // Reset all numbering
  for( i = 0; i < maxLevels; i++ ) {
    if( startFrom != -1 ) {
      levels[i] = startFrom - 1;
    } else if( renumber ) // start again
    { levels[i] = 0; }
    else
    { levels[i] = -1; }   // start from the number we found, if any
  }
  wxASSERT( !specifyLevel || ( specifyLevel && ( specifiedLevel >= 0 ) ) );
  // If we are associated with a control, make undoable; otherwise, apply immediately
  // to the data.
  bool haveControl = ( GetRichTextCtrl() != NULL );
  wxRichTextAction* action = NULL;
  if( haveControl && withUndo ) {
    action = new wxRichTextAction( NULL, _( "Renumber List" ), wxRICHTEXT_CHANGE_STYLE, & GetRichTextCtrl()->GetBuffer(), GetRichTextCtrl() );
    action->SetRange( range );
    action->SetPosition( GetRichTextCtrl()->GetCaretPosition() );
  }
  wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
  while( node ) {
    wxRichTextParagraph* para = wxDynamicCast( node->GetData(), wxRichTextParagraph );
    wxASSERT( para != NULL );
    if( para && para->GetChildCount() > 0 ) {
      // Stop searching if we're beyond the range of interest
      if( para->GetRange().GetStart() > range.GetEnd() ) {
        break;
      }
      if( !para->GetRange().IsOutside( range ) ) {
        // We'll be using a copy of the paragraph to make style changes,
        // not updating the buffer directly.
        wxRichTextParagraph* newPara wxDUMMY_INITIALIZE( NULL );
        if( haveControl && withUndo ) {
          newPara = new wxRichTextParagraph( *para );
          action->GetNewParagraphs().AppendChild( newPara );
          // Also store the old ones for Undo
          action->GetOldParagraphs().AppendChild( new wxRichTextParagraph( *para ) );
        } else
        { newPara = para; }
        wxRichTextListStyleDefinition* defToUse = def;
        if( !defToUse ) {
          if( styleSheet && !newPara->GetAttributes().GetListStyleName().IsEmpty() ) {
            defToUse = styleSheet->FindListStyle( newPara->GetAttributes().GetListStyleName() );
          }
        }
        if( defToUse ) {
          int thisIndent = newPara->GetAttributes().GetLeftIndent();
          int thisLevel = defToUse->FindLevelForIndent( thisIndent );
          // If we've specified a level to apply to all, change the level.
          if( specifiedLevel != -1 ) {
            thisLevel = specifiedLevel;
          }
          // Do promotion if specified
          if( ( promoteBy != 0 ) && !para->GetRange().IsOutside( promotionRange ) ) {
            thisLevel = thisLevel - promoteBy;
            if( thisLevel < 0 )
            { thisLevel = 0; }
            if( thisLevel > 9 )
            { thisLevel = 9; }
          }
          // Apply the overall list style, and item style for this level
          wxTextAttrEx listStyle( defToUse->GetCombinedStyleForLevel( thisLevel, styleSheet ) );
          wxRichTextApplyStyle( newPara->GetAttributes(), listStyle );
          // OK, we've (re)applied the style, now let's get the numbering right.
          if( currentLevel == -1 ) {
            currentLevel = thisLevel;
          }
          // Same level as before, do nothing except increment level's number afterwards
          if( currentLevel == thisLevel ) {
          }
          // A deeper level: start renumbering all levels after current level
          else if( thisLevel > currentLevel ) {
            for( i = currentLevel + 1; i <= thisLevel; i++ )
            { levels[i] = 0; }
            currentLevel = thisLevel;
          } else if( thisLevel < currentLevel ) {
            currentLevel = thisLevel;
          }
          // Use the current numbering if -1 and we have a bullet number already
          if( levels[currentLevel] == -1 ) {
            if( newPara->GetAttributes().HasBulletNumber() )
            { levels[currentLevel] = newPara->GetAttributes().GetBulletNumber(); }
            else
            { levels[currentLevel] = 1; }
          } else
          { levels[currentLevel] ++; }
          newPara->GetAttributes().SetBulletNumber( levels[currentLevel] );
          // Create the bullet text if an outline list
          if( listStyle.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE ) {
            wxString text;
            for( i = 0; i <= currentLevel; i++ ) {
              if( !text.IsEmpty() )
              { text += wxT( "." ); }
              text += wxString::Format( wxT( "%d" ), levels[i] );
            }
            newPara->GetAttributes().SetBulletText( text );
          }
        }
      }
    }
    node = node->GetNext();
  }
  if( haveControl && withUndo ) {
    GetRichTextCtrl()->GetBuffer().SubmitAction( action );
  }
  return true;
}

bool wxRichTextParagraphLayoutBox::NumberList( const wxRichTextRange& range, const wxString& defName, int flags, int startFrom, int specifiedLevel ) {
  if( GetStyleSheet() ) {
    wxRichTextListStyleDefinition* def = NULL;
    if( !defName.IsEmpty() ) {
      def = GetStyleSheet()->FindListStyle( defName );
    }
    return NumberList( range, def, flags, startFrom, specifiedLevel );
  }
  return false;
}

bool wxRichTextParagraphLayoutBox::PromoteList( int promoteBy, const wxRichTextRange& range, wxRichTextListStyleDefinition* def, int flags, int specifiedLevel ) {
  return DoNumberList( range, range, promoteBy, def, flags, 1, specifiedLevel );
}

bool wxRichTextParagraphLayoutBox::PromoteList( int promoteBy, const wxRichTextRange& range, const wxString& defName, int flags, int specifiedLevel ) {
  if( GetStyleSheet() ) {
    wxRichTextListStyleDefinition* def = NULL;
    if( !defName.IsEmpty() ) {
      def = GetStyleSheet()->FindListStyle( defName );
    }
    return PromoteList( promoteBy, range, def, flags, specifiedLevel );
  }
  return false;
}

/// Fills in the attributes for numbering a paragraph after previousParagraph. It also finds the
/// position of the paragraph that it had to start looking from.
bool wxRichTextParagraphLayoutBox::FindNextParagraphNumber( wxRichTextParagraph* previousParagraph, wxRichTextAttr& attr ) const {
  if( !previousParagraph->GetAttributes().HasFlag( wxTEXT_ATTR_BULLET_STYLE ) || previousParagraph->GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE ) {
    return false;
  }
  wxRichTextStyleSheet* styleSheet = GetStyleSheet();
  if( styleSheet && !previousParagraph->GetAttributes().GetListStyleName().IsEmpty() ) {
    wxRichTextListStyleDefinition* def = styleSheet->FindListStyle( previousParagraph->GetAttributes().GetListStyleName() );
    if( def ) {
      // int thisIndent = previousParagraph->GetAttributes().GetLeftIndent();
      // int thisLevel = def->FindLevelForIndent(thisIndent);
      bool isOutline = ( previousParagraph->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE ) != 0;
      attr.SetFlags( previousParagraph->GetAttributes().GetFlags() & ( wxTEXT_ATTR_BULLET_STYLE | wxTEXT_ATTR_BULLET_NUMBER | wxTEXT_ATTR_BULLET_TEXT | wxTEXT_ATTR_BULLET_NAME ) );
      if( previousParagraph->GetAttributes().HasBulletName() ) {
        attr.SetBulletName( previousParagraph->GetAttributes().GetBulletName() );
      }
      attr.SetBulletStyle( previousParagraph->GetAttributes().GetBulletStyle() );
      attr.SetListStyleName( previousParagraph->GetAttributes().GetListStyleName() );
      int nextNumber = previousParagraph->GetAttributes().GetBulletNumber() + 1;
      attr.SetBulletNumber( nextNumber );
      if( isOutline ) {
        wxString text = previousParagraph->GetAttributes().GetBulletText();
        if( !text.IsEmpty() ) {
          int pos = text.Find( wxT( '.' ), true );
          if( pos != wxNOT_FOUND ) {
            text = text.Mid( 0, text.Length() - pos - 1 );
          } else
          { text = wxEmptyString; }
          if( !text.IsEmpty() ) {
            text += wxT( "." );
          }
          text += wxString::Format( wxT( "%d" ), nextNumber );
          attr.SetBulletText( text );
        }
      }
      return true;
    } else
    { return false; }
  } else
  { return false; }
}

/*!
  wxRichTextParagraph
  This object represents a single paragraph (or in a straight text editor, a line).
*/

IMPLEMENT_DYNAMIC_CLASS( wxRichTextParagraph, wxRichTextBox )

wxArrayInt wxRichTextParagraph::sm_defaultTabs;

wxRichTextParagraph::wxRichTextParagraph( wxRichTextObject* parent, wxTextAttrEx* style ):
  wxRichTextBox( parent ) {
  if( style ) {
    SetAttributes( *style );
  }
}

wxRichTextParagraph::wxRichTextParagraph( const wxString& text, wxRichTextObject* parent, wxTextAttrEx* paraStyle, wxTextAttrEx* charStyle ):
  wxRichTextBox( parent ) {
  if( paraStyle ) {
    SetAttributes( *paraStyle );
  }
  AppendChild( new wxRichTextPlainText( text, this, charStyle ) );
}

wxRichTextParagraph::~wxRichTextParagraph() {
  ClearLines();
}

/// Draw the item
bool wxRichTextParagraph::Draw( wxDC& dc, const wxRichTextRange& range, const wxRichTextRange& selectionRange, const wxRect& rect, int ( descent ), int style ) {
  wxTextAttrEx attr = GetCombinedAttributes();
  // Draw the bullet, if any
  if( attr.GetBulletStyle() != wxTEXT_ATTR_BULLET_STYLE_NONE ) {
    if( attr.GetLeftSubIndent() != 0 ) {
      int spaceBeforePara = ConvertTenthsMMToPixels( dc, attr.GetParagraphSpacingBefore() );
      int leftIndent = ConvertTenthsMMToPixels( dc, attr.GetLeftIndent() );
      wxTextAttrEx bulletAttr( GetCombinedAttributes() );
      // Combine with the font of the first piece of content, if one is specified
      if( GetChildren().GetCount() > 0 ) {
        wxRichTextObject* firstObj = ( wxRichTextObject* ) GetChildren().GetFirst()->GetData();
        if( firstObj->GetAttributes().HasFont() ) {
          wxRichTextApplyStyle( bulletAttr, firstObj->GetAttributes() );
        }
      }
      // Get line height from first line, if any
      wxRichTextLine* line = m_cachedLines.GetFirst() ? ( wxRichTextLine* ) m_cachedLines.GetFirst()->GetData() : ( wxRichTextLine* ) NULL;
      wxPoint linePos;
      int lineHeight wxDUMMY_INITIALIZE( 0 );
      if( line ) {
        lineHeight = line->GetSize().y;
        linePos = line->GetPosition() + GetPosition();
      } else {
        wxFont font;
        if( bulletAttr.GetFont().Ok() ) {
          font = bulletAttr.GetFont();
        } else
        { font = ( *wxNORMAL_FONT ); }
        wxCheckSetFont( dc, font );
        lineHeight = dc.GetCharHeight();
        linePos = GetPosition();
        linePos.y += spaceBeforePara;
      }
      wxRect bulletRect( GetPosition().x + leftIndent, linePos.y, linePos.x - ( GetPosition().x + leftIndent ), lineHeight );
      if( attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP ) {
        if( wxRichTextBuffer::GetRenderer() ) {
          wxRichTextBuffer::GetRenderer()->DrawBitmapBullet( this, dc, bulletAttr, bulletRect );
        }
      } else if( attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_STANDARD ) {
        if( wxRichTextBuffer::GetRenderer() ) {
          wxRichTextBuffer::GetRenderer()->DrawStandardBullet( this, dc, bulletAttr, bulletRect );
        }
      } else {
        wxString bulletText = GetBulletText();
        if( !bulletText.empty() && wxRichTextBuffer::GetRenderer() ) {
          wxRichTextBuffer::GetRenderer()->DrawTextBullet( this, dc, bulletAttr, bulletRect, bulletText );
        }
      }
    }
  }
  // Draw the range for each line, one object at a time.
  wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
  while( node ) {
    wxRichTextLine* line = node->GetData();
    wxRichTextRange lineRange = line->GetAbsoluteRange();
    // Lines are specified relative to the paragraph
    wxPoint linePosition = line->GetPosition() + GetPosition();
    // Don't draw if off the screen
    if( ( ( style & wxRICHTEXT_DRAW_IGNORE_CACHE ) != 0 ) || ( ( linePosition.y + line->GetSize().y ) >= rect.y && linePosition.y <= rect.y + rect.height ) ) {
      wxPoint objectPosition = linePosition;
      int maxDescent = line->GetDescent();
      // Loop through objects until we get to the one within range
      wxRichTextObjectList::compatibility_iterator node2 = m_children.GetFirst();
      int i = 0;
      while( node2 ) {
        wxRichTextObject* child = node2->GetData();
        if( child->GetRange().GetLength() > 0 && !child->GetRange().IsOutside( lineRange ) && !lineRange.IsOutside( range ) ) {
          // Draw this part of the line at the correct position
          wxRichTextRange objectRange( child->GetRange() );
          objectRange.LimitTo( lineRange );
          wxSize objectSize;
          #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING && wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
          if( i < ( int ) line->GetObjectSizes().GetCount() ) {
            objectSize.x = line->GetObjectSizes()[( size_t ) i];
          } else
          #endif
          {
            int descent = 0;
            child->GetRangeSize( objectRange, objectSize, descent, dc, wxRICHTEXT_UNFORMATTED, objectPosition );
          }
          // Use the child object's width, but the whole line's height
          wxRect childRect( objectPosition, wxSize( objectSize.x, line->GetSize().y ) );
          child->Draw( dc, objectRange, selectionRange, childRect, maxDescent, style );
          objectPosition.x += objectSize.x;
          i ++;
        } else if( child->GetRange().GetStart() > lineRange.GetEnd() )
          // Can break out of inner loop now since we've passed this line's range
        { break; }
        node2 = node2->GetNext();
      }
    }
    node = node->GetNext();
  }
  return true;
}

// Get the range width using partial extents calculated for the whole paragraph.
static int wxRichTextGetRangeWidth( const wxRichTextParagraph& para, const wxRichTextRange& range, const wxArrayInt& partialExtents ) {
  wxASSERT( partialExtents.GetCount() >= ( size_t ) range.GetLength() );
  if( partialExtents.GetCount() < ( size_t ) range.GetLength() ) {
    return 0;
  }
  int leftMostPos = 0;
  if( range.GetStart() - para.GetRange().GetStart() > 0 ) {
    leftMostPos = partialExtents[range.GetStart() - para.GetRange().GetStart() - 1];
  }
  int rightMostPos = partialExtents[range.GetEnd() - para.GetRange().GetStart()];
  int w = rightMostPos - leftMostPos;
  return w;
}

static wxDC* g_globalDC = NULL;

/// Lay the item out
bool wxRichTextParagraph::Layout( wxDC& dc, const wxRect& rect, int style ) {
  wxTextAttrEx attr = GetCombinedAttributes();
  // ClearLines();
  // Increase the size of the paragraph due to spacing
  int spaceBeforePara = ConvertTenthsMMToPixels( dc, attr.GetParagraphSpacingBefore() );
  int spaceAfterPara = ConvertTenthsMMToPixels( dc, attr.GetParagraphSpacingAfter() );
  int leftIndent = ConvertTenthsMMToPixels( dc, attr.GetLeftIndent() );
  int leftSubIndent = ConvertTenthsMMToPixels( dc, attr.GetLeftSubIndent() );
  int rightIndent = ConvertTenthsMMToPixels( dc, attr.GetRightIndent() );
  int lineSpacing = 0;
  // Let's assume line spacing of 10 is normal, 15 is 1.5, 20 is 2, etc.
  if( attr.HasLineSpacing() && attr.GetLineSpacing() > 0 && attr.GetFont().Ok() ) {
    wxCheckSetFont( dc, attr.GetFont() );
    lineSpacing = ( int )( double( dc.GetCharHeight() ) * ( double( attr.GetLineSpacing() ) / 10.0 - 1.0 ) );
  }
  // Available space for text on each line differs.
  int availableTextSpaceFirstLine = rect.GetWidth() - leftIndent - rightIndent;
  // Bullets start the text at the same position as subsequent lines
  if( attr.GetBulletStyle() != wxTEXT_ATTR_BULLET_STYLE_NONE ) {
    availableTextSpaceFirstLine -= leftSubIndent;
  }
  int availableTextSpaceSubsequentLines = rect.GetWidth() - leftIndent - rightIndent - leftSubIndent;
  // Start position for each line relative to the paragraph
  int startPositionFirstLine = leftIndent;
  int startPositionSubsequentLines = leftIndent + leftSubIndent;
  // If we have a bullet in this paragraph, the start position for the first line's text
  // is actually leftIndent + leftSubIndent.
  if( attr.GetBulletStyle() != wxTEXT_ATTR_BULLET_STYLE_NONE ) {
    startPositionFirstLine = startPositionSubsequentLines;
  }
  long lastEndPos = GetRange().GetStart() - 1;
  long lastCompletedEndPos = lastEndPos;
  int currentWidth = 0;
  SetPosition( rect.GetPosition() );
  wxPoint currentPosition( 0, spaceBeforePara ); // We will calculate lines relative to paragraph
  int lineHeight = 0;
  int maxWidth = 0;
  int maxDescent = 0;
  int maxAscent = 0;
  int lineCount = 0;
  wxRichTextObjectList::compatibility_iterator node;
  #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
  wxUnusedVar( style );
  g_GlobalPartialTextExtents.Clear();
  g_UseGlobalPartialTextExtents = true;
  wxSize paraSize;
  int paraDescent;
  // This calculates the partial text extents
  GetRangeSize( GetRange(), paraSize, paraDescent, dc, wxRICHTEXT_UNFORMATTED | wxRICHTEXT_CACHE_SIZE, rect.GetPosition() );
  g_UseGlobalPartialTextExtents = false;
  #else
  node = m_children.GetFirst();
  while( node ) {
    wxRichTextObject* child = node->GetData();
    child->SetCachedSize( wxDefaultSize );
    child->Layout( dc, rect, style );
    node = node->GetNext();
  }
  #endif
  // Split up lines
  // We may need to go back to a previous child, in which case create the new line,
  // find the child corresponding to the start position of the string, and
  // continue.
  node = m_children.GetFirst();
  while( node ) {
    wxRichTextObject* child = node->GetData();
    if( child->GetRange().GetLength() == 0 ) {
      node = node->GetNext();
      continue;
    }
    // If this is e.g. a composite text box, it will need to be laid out itself.
    // But if just a text fragment or image, for example, this will
    // do nothing. NB: won't we need to set the position after layout?
    // since for example if position is dependent on vertical line size, we
    // can't tell the position until the size is determined. So possibly introduce
    // another layout phase.
    // Available width depends on whether we're on the first or subsequent lines
    int availableSpaceForText = ( lineCount == 0 ? availableTextSpaceFirstLine : availableTextSpaceSubsequentLines );
    currentPosition.x = ( lineCount == 0 ? startPositionFirstLine : startPositionSubsequentLines );
    // We may only be looking at part of a child, if we searched back for wrapping
    // and found a suitable point some way into the child. So get the size for the fragment
    // if necessary.
    long nextBreakPos = GetFirstLineBreakPosition( lastEndPos + 1 );
    long lastPosToUse = child->GetRange().GetEnd();
    bool lineBreakInThisObject = ( nextBreakPos > -1 && nextBreakPos <= child->GetRange().GetEnd() );
    if( lineBreakInThisObject ) {
      lastPosToUse = nextBreakPos;
    }
    wxSize childSize;
    int childDescent = 0;
    if( ( nextBreakPos == -1 ) && ( lastEndPos == child->GetRange().GetStart() - 1 ) ) { // i.e. we want to get the whole thing
      childSize = child->GetCachedSize();
      childDescent = child->GetDescent();
    } else {
      #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
      // Get height only, then the width using the partial extents
      GetRangeSize( wxRichTextRange( lastEndPos + 1, lastPosToUse ), childSize, childDescent, dc, wxRICHTEXT_UNFORMATTED | wxRICHTEXT_HEIGHT_ONLY );
      childSize.x = wxRichTextGetRangeWidth( *this, wxRichTextRange( lastEndPos + 1, lastPosToUse ), g_GlobalPartialTextExtents );
      #else
      GetRangeSize( wxRichTextRange( lastEndPos + 1, lastPosToUse ), childSize, childDescent, dc, wxRICHTEXT_UNFORMATTED, rect.GetPosition() );
      #endif
    }
    // Cases:
    // 1) There was a line break BEFORE the natural break
    // 2) There was a line break AFTER the natural break
    // 3) The child still fits (carry on)
    if( ( lineBreakInThisObject && ( childSize.x + currentWidth <= availableSpaceForText ) ) ||
        ( childSize.x + currentWidth > availableSpaceForText ) ) {
      long wrapPosition = 0;
      // Find a place to wrap. This may walk back to previous children,
      // for example if a word spans several objects.
      if( !FindWrapPosition( wxRichTextRange( lastCompletedEndPos + 1, child->GetRange().GetEnd() ), dc, availableSpaceForText, wrapPosition ) ) {
        // If the function failed, just cut it off at the end of this child.
        wrapPosition = child->GetRange().GetEnd();
      }
      // FindWrapPosition can still return a value that will put us in an endless wrapping loop
      if( wrapPosition <= lastCompletedEndPos ) {
        wrapPosition = wxMax( lastCompletedEndPos + 1, child->GetRange().GetEnd() );
      }
      // wxLogDebug(wxT("Split at %ld"), wrapPosition);
      // Let's find the actual size of the current line now
      wxSize actualSize;
      wxRichTextRange actualRange( lastCompletedEndPos + 1, wrapPosition );
      /// Use previous descent, not the wrapping descent we just found, since this may be too big
      /// for the fragment we're about to add.
      childDescent = maxDescent;
      #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
      // Get height only, then the width using the partial extents
      GetRangeSize( actualRange, actualSize, childDescent, dc, wxRICHTEXT_UNFORMATTED | wxRICHTEXT_HEIGHT_ONLY );
      actualSize.x = wxRichTextGetRangeWidth( *this, actualRange, g_GlobalPartialTextExtents );
      #else
      GetRangeSize( actualRange, actualSize, childDescent, dc, wxRICHTEXT_UNFORMATTED );
      #endif
      currentWidth = actualSize.x;
      maxDescent = wxMax( childDescent, maxDescent );
      maxAscent = wxMax( actualSize.y - childDescent, maxAscent );
      lineHeight = maxDescent + maxAscent;
      // Add a new line
      wxRichTextLine* line = AllocateLine( lineCount );
      // Set relative range so we won't have to change line ranges when paragraphs are moved
      line->SetRange( wxRichTextRange( actualRange.GetStart() - GetRange().GetStart(), actualRange.GetEnd() - GetRange().GetStart() ) );
      line->SetPosition( currentPosition );
      line->SetSize( wxSize( currentWidth, lineHeight ) );
      line->SetDescent( maxDescent );
      // Now move down a line. TODO: add margins, spacing
      currentPosition.y += lineHeight;
      currentPosition.y += lineSpacing;
      currentWidth = 0;
      maxDescent = 0;
      maxAscent = 0;
      maxWidth = wxMax( maxWidth, currentWidth );
      lineCount ++;
      wxASSERT( wrapPosition > lastCompletedEndPos );
      lastEndPos = wrapPosition;
      lastCompletedEndPos = lastEndPos;
      lineHeight = 0;
      // May need to set the node back to a previous one, due to searching back in wrapping
      wxRichTextObject* childAfterWrapPosition = FindObjectAtPosition( wrapPosition + 1 );
      if( childAfterWrapPosition ) {
        node = m_children.Find( childAfterWrapPosition );
      } else
      { node = node->GetNext(); }
    } else {
      // We still fit, so don't add a line, and keep going
      currentWidth += childSize.x;
      maxDescent = wxMax( childDescent, maxDescent );
      maxAscent = wxMax( childSize.y - childDescent, maxAscent );
      lineHeight = maxDescent + maxAscent;
      maxWidth = wxMax( maxWidth, currentWidth );
      lastEndPos = child->GetRange().GetEnd();
      node = node->GetNext();
    }
  }
  // Add the last line - it's the current pos -> last para pos
  // Substract -1 because the last position is always the end-paragraph position.
  if( lastCompletedEndPos <= GetRange().GetEnd() - 1 ) {
    currentPosition.x = ( lineCount == 0 ? startPositionFirstLine : startPositionSubsequentLines );
    wxRichTextLine* line = AllocateLine( lineCount );
    wxRichTextRange actualRange( lastCompletedEndPos + 1, GetRange().GetEnd() - 1 );
    // Set relative range so we won't have to change line ranges when paragraphs are moved
    line->SetRange( wxRichTextRange( actualRange.GetStart() - GetRange().GetStart(), actualRange.GetEnd() - GetRange().GetStart() ) );
    line->SetPosition( currentPosition );
    if( lineHeight == 0 ) {
      if( attr.GetFont().Ok() ) {
        wxCheckSetFont( dc, attr.GetFont() );
      }
      lineHeight = dc.GetCharHeight();
    }
    if( maxDescent == 0 ) {
      int w, h;
      dc.GetTextExtent( wxT( "X" ), & w, &h, & maxDescent );
    }
    line->SetSize( wxSize( currentWidth, lineHeight ) );
    line->SetDescent( maxDescent );
    currentPosition.y += lineHeight;
    currentPosition.y += lineSpacing;
    lineCount ++;
  }
  // Remove remaining unused line objects, if any
  ClearUnusedLines( lineCount );
  // Apply styles to wrapped lines
  g_globalDC = & dc;
  ApplyParagraphStyle( attr, rect );
  g_globalDC = NULL;
  SetCachedSize( wxSize( maxWidth, currentPosition.y + spaceAfterPara ) );
  m_dirty = false;
  #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
  #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
  // Use the text extents to calculate the size of each fragment in each line
  wxRichTextLineList::compatibility_iterator lineNode = m_cachedLines.GetFirst();
  while( lineNode ) {
    wxRichTextLine* line = lineNode->GetData();
    wxRichTextRange lineRange = line->GetAbsoluteRange();
    // Loop through objects until we get to the one within range
    wxRichTextObjectList::compatibility_iterator node2 = m_children.GetFirst();
    while( node2 ) {
      wxRichTextObject* child = node2->GetData();
      if( child->GetRange().GetLength() > 0 && !child->GetRange().IsOutside( lineRange ) ) {
        wxRichTextRange rangeToUse = lineRange;
        rangeToUse.LimitTo( child->GetRange() );
        // Find the size of the child from the text extents, and store in an array
        // for drawing later
        int left = 0;
        if( rangeToUse.GetStart() > GetRange().GetStart() ) {
          left = g_GlobalPartialTextExtents[( rangeToUse.GetStart() - 1 ) - GetRange().GetStart()];
        }
        int right = g_GlobalPartialTextExtents[rangeToUse.GetEnd() - GetRange().GetStart()];
        int sz = right - left;
        line->GetObjectSizes().Add( sz );
      } else if( child->GetRange().GetStart() > lineRange.GetEnd() )
        // Can break out of inner loop now since we've passed this line's range
      { break; }
      node2 = node2->GetNext();
    }
    lineNode = lineNode->GetNext();
  }
  #endif
  g_GlobalPartialTextExtents.Clear();
  #endif
  return true;
}

/// Apply paragraph styles, such as centering, to wrapped lines
void wxRichTextParagraph::ApplyParagraphStyle( const wxTextAttrEx& attr, const wxRect& rect ) {
  if( !attr.HasAlignment() ) {
    return;
  }
  wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
  while( node ) {
    wxRichTextLine* line = node->GetData();
    wxPoint pos = line->GetPosition();
    wxSize size = line->GetSize();
    // centering, right-justification
    if( attr.HasAlignment() && GetAttributes().GetAlignment() == wxTEXT_ALIGNMENT_CENTRE ) {
      int rightIndent = ConvertTenthsMMToPixels( * g_globalDC, attr.GetRightIndent() );
      pos.x = ( rect.GetWidth() - pos.x - rightIndent - size.x ) / 2 + pos.x;
      line->SetPosition( pos );
    } else if( attr.HasAlignment() && GetAttributes().GetAlignment() == wxTEXT_ALIGNMENT_RIGHT ) {
      int rightIndent = ConvertTenthsMMToPixels( * g_globalDC, attr.GetRightIndent() );
      pos.x = rect.GetWidth() - size.x - rightIndent;
      line->SetPosition( pos );
    }
    node = node->GetNext();
  }
}

/// Insert text at the given position
bool wxRichTextParagraph::InsertText( long pos, const wxString& text ) {
  wxRichTextObject* childToUse = NULL;
  wxRichTextObjectList::compatibility_iterator nodeToUse = wxRichTextObjectList::compatibility_iterator();
  wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
  while( node ) {
    wxRichTextObject* child = node->GetData();
    if( child->GetRange().Contains( pos ) && child->GetRange().GetLength() > 0 ) {
      childToUse = child;
      nodeToUse = node;
      break;
    }
    node = node->GetNext();
  }
  if( childToUse ) {
    wxRichTextPlainText* textObject = wxDynamicCast( childToUse, wxRichTextPlainText );
    if( textObject ) {
      int posInString = pos - textObject->GetRange().GetStart();
      wxString newText = textObject->GetText().Mid( 0, posInString ) +
                         text + textObject->GetText().Mid( posInString );
      textObject->SetText( newText );
      int textLength = text.length();
      textObject->SetRange( wxRichTextRange( textObject->GetRange().GetStart(),
                                             textObject->GetRange().GetEnd() + textLength ) );
      wxRichTextObjectList::compatibility_iterator node = nodeToUse->GetNext();
      while( node ) {
        wxRichTextObject* child = node->GetData();
        child->SetRange( wxRichTextRange( textObject->GetRange().GetStart() + textLength,
                                          textObject->GetRange().GetEnd() + textLength ) );
        node = node->GetNext();
      }
      return true;
    } else {
    }
  } else {
    wxRichTextPlainText* textObject = new wxRichTextPlainText( text, this );
    AppendChild( textObject );
    return true;
  }
  return false;
}

void wxRichTextParagraph::Copy( const wxRichTextParagraph& obj ) {
  wxRichTextBox::Copy( obj );
}

void wxRichTextParagraph::ClearLines() {
  WX_CLEAR_LIST( wxRichTextLineList, m_cachedLines );
}

bool wxRichTextParagraph::GetRangeSize( const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, int flags, wxPoint position ) const {
  if( !range.IsWithin( GetRange() ) ) {
    return false;
  }
  if( flags & wxRICHTEXT_UNFORMATTED ) {
    wxSize sz;
    wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
    while( node ) {
      wxRichTextObject* child = node->GetData();
      if( !child->GetRange().IsOutside( range ) ) {
        wxSize childSize;
        wxRichTextRange rangeToUse = range;
        rangeToUse.LimitTo( child->GetRange() );
        int childDescent = 0;
        // At present wxRICHTEXT_HEIGHT_ONLY is only fast if we're already cached the size,
        // but it's only going to be used after caching has taken place.
        if( ( flags & wxRICHTEXT_HEIGHT_ONLY ) && child->GetCachedSize().y != 0 ) {
          childDescent = child->GetDescent();
          childSize = child->GetCachedSize();
          sz.y = wxMax( sz.y, childSize.y );
          sz.x += childSize.x;
          descent = wxMax( descent, childDescent );
        } else if( child->GetRangeSize( rangeToUse, childSize, childDescent, dc, flags, wxPoint( position.x + sz.x, position.y ) ) ) {
          sz.y = wxMax( sz.y, childSize.y );
          sz.x += childSize.x;
          descent = wxMax( descent, childDescent );
          if( ( flags & wxRICHTEXT_CACHE_SIZE ) && ( rangeToUse == child->GetRange() ) ) {
            child->SetCachedSize( childSize );
            child->SetDescent( childDescent );
          }
        }
      }
      node = node->GetNext();
    }
    size = sz;
  } else {
    // Use formatted data, with line breaks
    wxSize sz;
    // We're going to loop through each line, and then for each line,
    // call GetRangeSize for the fragment that comprises that line.
    // Only we have to do that multiple times within the line, because
    // the line may be broken into pieces. For now ignore line break commands
    // (so we can assume that getting the unformatted size for a fragment
    // within a line is the actual size)
    wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
    while( node ) {
      wxRichTextLine* line = node->GetData();
      wxRichTextRange lineRange = line->GetAbsoluteRange();
      if( !lineRange.IsOutside( range ) ) {
        wxSize lineSize;
        wxRichTextObjectList::compatibility_iterator node2 = m_children.GetFirst();
        while( node2 ) {
          wxRichTextObject* child = node2->GetData();
          if( !child->GetRange().IsOutside( lineRange ) ) {
            wxRichTextRange rangeToUse = lineRange;
            rangeToUse.LimitTo( child->GetRange() );
            wxSize childSize;
            int childDescent = 0;
            if( child->GetRangeSize( rangeToUse, childSize, childDescent, dc, flags, wxPoint( position.x + sz.x, position.y ) ) ) {
              lineSize.y = wxMax( lineSize.y, childSize.y );
              lineSize.x += childSize.x;
            }
            descent = wxMax( descent, childDescent );
          }
          node2 = node2->GetNext();
        }
        // Increase size by a line (TODO: paragraph spacing)
        sz.y += lineSize.y;
        sz.x = wxMax( sz.x, lineSize.x );
      }
      node = node->GetNext();
    }
    size = sz;
  }
  return true;
}

/// Finds the absolute position and row height for the given character position
bool wxRichTextParagraph::FindPosition( wxDC& dc, long index, wxPoint& pt, int* height, bool forceLineStart ) {
  if( index == -1 ) {
    wxRichTextLine* line = ( ( wxRichTextParagraphLayoutBox* )GetParent() )->GetLineAtPosition( 0 );
    if( line ) {
      *height = line->GetSize().y;
    } else
    { *height = dc.GetCharHeight(); }
    // -1 means 'the start of the buffer'.
    pt = GetPosition();
    if( line ) {
      pt = pt + line->GetPosition();
    }
    return true;
  }
  // The final position in a paragraph is taken to mean the position
  // at the start of the next paragraph.
  if( index == GetRange().GetEnd() ) {
    wxRichTextParagraphLayoutBox* parent = wxDynamicCast( GetParent(), wxRichTextParagraphLayoutBox );
    wxASSERT( parent != NULL );
    // Find the height at the next paragraph, if any
    wxRichTextLine* line = parent->GetLineAtPosition( index + 1 );
    if( line ) {
      *height = line->GetSize().y;
      pt = line->GetAbsolutePosition();
    } else {
      *height = dc.GetCharHeight();
      int indent = ConvertTenthsMMToPixels( dc, m_attributes.GetLeftIndent() );
      pt = wxPoint( indent, GetCachedSize().y );
    }
    return true;
  }
  if( index < GetRange().GetStart() || index > GetRange().GetEnd() ) {
    return false;
  }
  wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
  while( node ) {
    wxRichTextLine* line = node->GetData();
    wxRichTextRange lineRange = line->GetAbsoluteRange();
    if( index >= lineRange.GetStart() && index <= lineRange.GetEnd() ) {
      // If this is the last point in the line, and we're forcing the
      // returned value to be the start of the next line, do the required
      // thing.
      if( index == lineRange.GetEnd() && forceLineStart ) {
        if( node->GetNext() ) {
          wxRichTextLine* nextLine = node->GetNext()->GetData();
          *height = nextLine->GetSize().y;
          pt = nextLine->GetAbsolutePosition();
          return true;
        }
      }
      pt.y = line->GetPosition().y + GetPosition().y;
      wxRichTextRange r( lineRange.GetStart(), index );
      wxSize rangeSize;
      int descent = 0;
      // We find the size of the line up to this point,
      // then we can add this size to the line start position and
      // paragraph start position to find the actual position.
      if( GetRangeSize( r, rangeSize, descent, dc, wxRICHTEXT_UNFORMATTED, line->GetPosition() + GetPosition() ) ) {
        pt.x = line->GetPosition().x + GetPosition().x + rangeSize.x;
        *height = line->GetSize().y;
        return true;
      }
    }
    node = node->GetNext();
  }
  return false;
}

/// Hit-testing: returns a flag indicating hit test details, plus
/// information about position
int wxRichTextParagraph::HitTest( wxDC& dc, const wxPoint& pt, long& textPosition ) {
  wxPoint paraPos = GetPosition();
  wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
  while( node ) {
    wxRichTextLine* line = node->GetData();
    wxPoint linePos = paraPos + line->GetPosition();
    wxSize lineSize = line->GetSize();
    wxRichTextRange lineRange = line->GetAbsoluteRange();
    if( pt.y <= linePos.y + lineSize.y ) {
      if( pt.x < linePos.x ) {
        textPosition = lineRange.GetStart();
        return wxRICHTEXT_HITTEST_BEFORE | wxRICHTEXT_HITTEST_OUTSIDE;
      } else if( pt.x >= ( linePos.x + lineSize.x ) ) {
        textPosition = lineRange.GetEnd();
        return wxRICHTEXT_HITTEST_AFTER | wxRICHTEXT_HITTEST_OUTSIDE;
      } else {
        #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
        g_GlobalPartialTextExtents.Clear();
        g_UseGlobalPartialTextExtents = true;
        wxSize paraSize;
        int paraDescent;
        // This calculates the partial text extents
        GetRangeSize( lineRange, paraSize, paraDescent, dc, wxRICHTEXT_UNFORMATTED, linePos );
        g_UseGlobalPartialTextExtents = false;
        int lastX = linePos.x;
        size_t i;
        for( i = 0; i < g_GlobalPartialTextExtents.GetCount(); i++ ) {
          int nextX = g_GlobalPartialTextExtents[i] + linePos.x;
          if( pt.x >= lastX && pt.x <= nextX ) {
            textPosition = i + lineRange.GetStart(); // minus 1?
            g_GlobalPartialTextExtents.Clear();
            // So now we know it's between i-1 and i.
            // Let's see if we can be more precise about
            // which side of the position it's on.
            int midPoint = ( nextX - lastX ) / 2 + lastX;
            if( pt.x >= midPoint )
            { return wxRICHTEXT_HITTEST_AFTER; }
            else
            { return wxRICHTEXT_HITTEST_BEFORE; }
          }
          lastX = nextX;
        }
        g_GlobalPartialTextExtents.Clear();
        #else
        long i;
        int lastX = linePos.x;
        for( i = lineRange.GetStart(); i <= lineRange.GetEnd(); i++ ) {
          wxSize childSize;
          int descent = 0;
          wxRichTextRange rangeToUse( lineRange.GetStart(), i );
          GetRangeSize( rangeToUse, childSize, descent, dc, wxRICHTEXT_UNFORMATTED, linePos );
          int nextX = childSize.x + linePos.x;
          if( pt.x >= lastX && pt.x <= nextX ) {
            textPosition = i;
            // So now we know it's between i-1 and i.
            // Let's see if we can be more precise about
            // which side of the position it's on.
            int midPoint = ( nextX - lastX ) / 2 + lastX;
            if( pt.x >= midPoint )
            { return wxRICHTEXT_HITTEST_AFTER; }
            else
            { return wxRICHTEXT_HITTEST_BEFORE; }
          } else
          { lastX = nextX; }
        }
        #endif
      }
    }
    node = node->GetNext();
  }
  return wxRICHTEXT_HITTEST_NONE;
}

/// Split an object at this position if necessary, and return
/// the previous object, or NULL if inserting at beginning.
wxRichTextObject* wxRichTextParagraph::SplitAt( long pos, wxRichTextObject** previousObject ) {
  wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
  while( node ) {
    wxRichTextObject* child = node->GetData();
    if( pos == child->GetRange().GetStart() ) {
      if( previousObject ) {
        if( node->GetPrevious() ) {
          *previousObject = node->GetPrevious()->GetData();
        } else
        { *previousObject = NULL; }
      }
      return child;
    }
    if( child->GetRange().Contains( pos ) ) {
      // This should create a new object, transferring part of
      // the content to the old object and the rest to the new object.
      wxRichTextObject* newObject = child->DoSplit( pos );
      // If we couldn't split this object, just insert in front of it.
      if( !newObject ) {
        // Maybe this is an empty string, try the next one
        // return child;
      } else {
        // Insert the new object after 'child'
        if( node->GetNext() ) {
          m_children.Insert( node->GetNext(), newObject );
        } else
        { m_children.Append( newObject ); }
        newObject->SetParent( this );
        if( previousObject ) {
          *previousObject = child;
        }
        return newObject;
      }
    }
    node = node->GetNext();
  }
  if( previousObject ) {
    *previousObject = NULL;
  }
  return NULL;
}

/// Move content to a list from obj on
void wxRichTextParagraph::MoveToList( wxRichTextObject* obj, wxList& list ) {
  wxRichTextObjectList::compatibility_iterator node = m_children.Find( obj );
  while( node ) {
    wxRichTextObject* child = node->GetData();
    list.Append( child );
    wxRichTextObjectList::compatibility_iterator oldNode = node;
    node = node->GetNext();
    m_children.DeleteNode( oldNode );
  }
}

/// Add content back from list
void wxRichTextParagraph::MoveFromList( wxList& list ) {
  for( wxList::compatibility_iterator node = list.GetFirst(); node; node = node->GetNext() ) {
    AppendChild( ( wxRichTextObject* ) node->GetData() );
  }
}

/// Calculate range
void wxRichTextParagraph::CalculateRange( long start, long& end ) {
  wxRichTextCompositeObject::CalculateRange( start, end );
  // Add one for end of paragraph
  end ++;
  m_range.SetRange( start, end );
}

/// Find the object at the given position
wxRichTextObject* wxRichTextParagraph::FindObjectAtPosition( long position ) {
  wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
  while( node ) {
    wxRichTextObject* obj = node->GetData();
    if( obj->GetRange().Contains( position ) ) {
      return obj;
    }
    node = node->GetNext();
  }
  return NULL;
}

/// Get the plain text searching from the start or end of the range.
/// The resulting string may be shorter than the range given.
bool wxRichTextParagraph::GetContiguousPlainText( wxString& text, const wxRichTextRange& range, bool fromStart ) {
  text = wxEmptyString;
  if( fromStart ) {
    wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
    while( node ) {
      wxRichTextObject* obj = node->GetData();
      if( !obj->GetRange().IsOutside( range ) ) {
        wxRichTextPlainText* textObj = wxDynamicCast( obj, wxRichTextPlainText );
        if( textObj ) {
          text += textObj->GetTextForRange( range );
        } else
        { text += wxT( " " ); }
      }
      node = node->GetNext();
    }
  } else {
    wxRichTextObjectList::compatibility_iterator node = m_children.GetLast();
    while( node ) {
      wxRichTextObject* obj = node->GetData();
      if( !obj->GetRange().IsOutside( range ) ) {
        wxRichTextPlainText* textObj = wxDynamicCast( obj, wxRichTextPlainText );
        if( textObj ) {
          text = textObj->GetTextForRange( range ) + text;
        } else
        { text = wxT( " " ) + text; }
      }
      node = node->GetPrevious();
    }
  }
  return true;
}

/// Find a suitable wrap position.
bool wxRichTextParagraph::FindWrapPosition( const wxRichTextRange& range, wxDC& dc, int availableSpace, long& wrapPosition ) {
  if( range.GetLength() <= 0 ) {
    return false;
  }
  // Find the first position where the line exceeds the available space.
  wxSize sz;
  long breakPosition = range.GetEnd();
  #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
  if( g_GlobalPartialTextExtents.GetCount() >= ( size_t )( GetRange().GetLength() - 1 ) ) { // the final position in a paragraph is the newline
    int widthBefore;
    if( range.GetStart() > GetRange().GetStart() ) {
      widthBefore = g_GlobalPartialTextExtents[range.GetStart() - GetRange().GetStart() - 1];
    } else
    { widthBefore = 0; }
    size_t i;
    for( i = ( size_t ) range.GetStart(); i <= ( size_t ) range.GetEnd(); i++ ) {
      int widthFromStartOfThisRange = g_GlobalPartialTextExtents[i - GetRange().GetStart()] - widthBefore;
      if( widthFromStartOfThisRange > availableSpace ) {
        breakPosition = i - 1;
        break;
      }
    }
  } else
  #endif
  {
    // Binary chop for speed
    long minPos = range.GetStart();
    long maxPos = range.GetEnd();
    while( true ) {
      if( minPos == maxPos ) {
        int descent = 0;
        GetRangeSize( wxRichTextRange( range.GetStart(), minPos ), sz, descent, dc, wxRICHTEXT_UNFORMATTED );
        if( sz.x > availableSpace ) {
          breakPosition = minPos - 1;
        }
        break;
      } else if( ( maxPos - minPos ) == 1 ) {
        int descent = 0;
        GetRangeSize( wxRichTextRange( range.GetStart(), minPos ), sz, descent, dc, wxRICHTEXT_UNFORMATTED );
        if( sz.x > availableSpace ) {
          breakPosition = minPos - 1;
        } else {
          GetRangeSize( wxRichTextRange( range.GetStart(), maxPos ), sz, descent, dc, wxRICHTEXT_UNFORMATTED );
          if( sz.x > availableSpace ) {
            breakPosition = maxPos - 1;
          }
        }
        break;
      } else {
        long nextPos = minPos + ( ( maxPos - minPos ) / 2 );
        int descent = 0;
        GetRangeSize( wxRichTextRange( range.GetStart(), nextPos ), sz, descent, dc, wxRICHTEXT_UNFORMATTED );
        if( sz.x > availableSpace ) {
          maxPos = nextPos;
        } else
        { minPos = nextPos; }
      }
    }
  }
  wxString plainText;
  if( GetContiguousPlainText( plainText, wxRichTextRange( range.GetStart(), breakPosition ), false ) ) {
    int newLinePos = plainText.Find( wxRichTextLineBreakChar );
    if( newLinePos != wxNOT_FOUND ) {
      breakPosition = wxMax( 0, range.GetStart() + newLinePos );
    } else {
      int spacePos = plainText.Find( wxT( ' ' ), true );
      int tabPos = plainText.Find( wxT( '\t' ), true );
      int pos = wxMax( spacePos, tabPos );
      if( pos != wxNOT_FOUND ) {
        int positionsFromEndOfString = plainText.length() - pos - 1;
        breakPosition = breakPosition - positionsFromEndOfString;
      }
    }
  }
  wrapPosition = breakPosition;
  return true;
}

wxString wxRichTextParagraph::GetBulletText() {
  if( GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE ||
      ( GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP ) ) {
    return wxEmptyString;
  }
  int number = GetAttributes().GetBulletNumber();
  wxString text;
  if( ( GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ARABIC ) || ( GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE ) ) {
    text.Printf( wxT( "%d" ), number );
  } else if( GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_UPPER ) {
    text.Printf( wxT( "%c" ), ( wxChar )( number + 64 ) );
  } else if( GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_LOWER ) {
    text.Printf( wxT( "%c" ), ( wxChar )( number + 96 ) );
  } else if( GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_UPPER ) {
    text = wxRichTextDecimalToRoman( number );
  } else if( GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_LOWER ) {
    text = wxRichTextDecimalToRoman( number );
    text.MakeLower();
  } else if( GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL ) {
    text = GetAttributes().GetBulletText();
  }
  if( GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE ) {
    if( !GetAttributes().GetBulletText().IsEmpty() ) {
      text = GetAttributes().GetBulletText();
    }
  }
  if( GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PARENTHESES ) {
    text = wxT( "(" ) + text + wxT( ")" );
  } else if( GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_RIGHT_PARENTHESIS ) {
    text = text + wxT( ")" );
  }
  if( GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PERIOD ) {
    text += wxT( "." );
  }
  return text;
}

/// Allocate or reuse a line object
wxRichTextLine* wxRichTextParagraph::AllocateLine( int pos ) {
  if( pos < ( int ) m_cachedLines.GetCount() ) {
    wxRichTextLine* line = m_cachedLines.Item( pos )->GetData();
    line->Init( this );
    return line;
  } else {
    wxRichTextLine* line = new wxRichTextLine( this );
    m_cachedLines.Append( line );
    return line;
  }
}

/// Clear remaining unused line objects, if any
bool wxRichTextParagraph::ClearUnusedLines( int lineCount ) {
  int cachedLineCount = m_cachedLines.GetCount();
  if( ( int ) cachedLineCount > lineCount ) {
    for( int i = 0; i < ( int )( cachedLineCount - lineCount ); i ++ ) {
      wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetLast();
      wxRichTextLine* line = node->GetData();
      m_cachedLines.Erase( node );
      delete line;
    }
  }
  return true;
}

/// Get combined attributes of the base style, paragraph style and character style. We use this to dynamically
/// retrieve the actual style.
wxTextAttrEx wxRichTextParagraph::GetCombinedAttributes( const wxTextAttrEx& contentStyle ) const {
  wxTextAttrEx attr;
  wxRichTextBuffer* buf = wxDynamicCast( GetParent(), wxRichTextBuffer );
  if( buf ) {
    attr = buf->GetBasicStyle();
    wxRichTextApplyStyle( attr, GetAttributes() );
  } else
  { attr = GetAttributes(); }
  wxRichTextApplyStyle( attr, contentStyle );
  return attr;
}

/// Get combined attributes of the base style and paragraph style.
wxTextAttrEx wxRichTextParagraph::GetCombinedAttributes() const {
  wxTextAttrEx attr;
  wxRichTextBuffer* buf = wxDynamicCast( GetParent(), wxRichTextBuffer );
  if( buf ) {
    attr = buf->GetBasicStyle();
    wxRichTextApplyStyle( attr, GetAttributes() );
  } else
  { attr = GetAttributes(); }
  return attr;
}

/// Create default tabstop array
void wxRichTextParagraph::InitDefaultTabs() {
  // create a default tab list at 10 mm each.
  for( int i = 0; i < 20; ++i ) {
    sm_defaultTabs.Add( i * 100 );
  }
}

/// Clear default tabstop array
void wxRichTextParagraph::ClearDefaultTabs() {
  sm_defaultTabs.Clear();
}

/// Get the first position from pos that has a line break character.
long wxRichTextParagraph::GetFirstLineBreakPosition( long pos ) {
  wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
  while( node ) {
    wxRichTextObject* obj = node->GetData();
    if( pos >= obj->GetRange().GetStart() && pos <= obj->GetRange().GetEnd() ) {
      wxRichTextPlainText* textObj = wxDynamicCast( obj, wxRichTextPlainText );
      if( textObj ) {
        long breakPos = textObj->GetFirstLineBreakPosition( pos );
        if( breakPos > -1 ) {
          return breakPos;
        }
      }
    }
    node = node->GetNext();
  }
  return -1;
}

/*!
  wxRichTextLine
  This object represents a line in a paragraph, and stores
  offsets from the start of the paragraph representing the
  start and end positions of the line.
*/

wxRichTextLine::wxRichTextLine( wxRichTextParagraph* parent ) {
  Init( parent );
}

/// Initialisation
void wxRichTextLine::Init( wxRichTextParagraph* parent ) {
  m_parent = parent;
  m_range.SetRange( -1, -1 );
  m_pos = wxPoint( 0, 0 );
  m_size = wxSize( 0, 0 );
  m_descent = 0;
  #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
  m_objectSizes.Clear();
  #endif
}

/// Copy
void wxRichTextLine::Copy( const wxRichTextLine& obj ) {
  m_range = obj.m_range;
  #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
  m_objectSizes = obj.m_objectSizes;
  #endif
}

/// Get the absolute object position
wxPoint wxRichTextLine::GetAbsolutePosition() const {
  return m_parent->GetPosition() + m_pos;
}

/// Get the absolute range
wxRichTextRange wxRichTextLine::GetAbsoluteRange() const {
  wxRichTextRange range( m_range.GetStart() + m_parent->GetRange().GetStart(), 0 );
  range.SetEnd( range.GetStart() + m_range.GetLength() - 1 );
  return range;
}

/*!
  wxRichTextPlainText
  This object represents a single piece of text.
*/

IMPLEMENT_DYNAMIC_CLASS( wxRichTextPlainText, wxRichTextObject )

wxRichTextPlainText::wxRichTextPlainText( const wxString& text, wxRichTextObject* parent, wxTextAttrEx* style ):
  wxRichTextObject( parent ) {
  if( style ) {
    SetAttributes( *style );
  }
  m_text = text;
}

#define USE_KERNING_FIX 1

// If insufficient tabs are defined, this is the tab width used
#define WIDTH_FOR_DEFAULT_TABS 50

/// Draw the item
bool wxRichTextPlainText::Draw( wxDC& dc, const wxRichTextRange& range, const wxRichTextRange& selectionRange, const wxRect& rect, int descent, int ( style ) ) {
  wxRichTextParagraph* para = wxDynamicCast( GetParent(), wxRichTextParagraph );
  wxASSERT( para != NULL );
  wxTextAttrEx textAttr( para ? para->GetCombinedAttributes( GetAttributes() ) : GetAttributes() );
  int offset = GetRange().GetStart();
  // Replace line break characters with spaces
  wxString str = m_text;
  wxString toRemove = wxRichTextLineBreakChar;
  str.Replace( toRemove, wxT( " " ) );
  if( textAttr.HasTextEffects() && ( textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_CAPITALS ) ) {
    str.MakeUpper();
  }
  long len = range.GetLength();
  wxString stringChunk = str.Mid( range.GetStart() - offset, ( size_t ) len );
  wxFont textFont = textAttr.GetFont();
  if( textFont.Ok() ) {
    wxCheckSetFont( dc, textFont );
  }
  int charHeight = dc.GetCharHeight();
  int x, y;
  if( textFont.Ok() ) {
    if( textAttr.HasTextEffects() && ( textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT ) ) {
      double size = static_cast<double>( textFont.GetPointSize() ) / wxSCRIPT_MUL_FACTOR;
      textFont.SetPointSize( static_cast<int>( size ) );
      x = rect.x;
      y = rect.y;
      wxCheckSetFont( dc, textFont );
    } else if( textAttr.HasTextEffects() && ( textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT ) ) {
      double size = static_cast<double>( textFont.GetPointSize() ) / wxSCRIPT_MUL_FACTOR;
      textFont.SetPointSize( static_cast<int>( size ) );
      x = rect.x;
      int sub_height = static_cast<int>( static_cast<double>( charHeight ) / wxSCRIPT_MUL_FACTOR );
      y = rect.y + ( rect.height - sub_height + ( descent - m_descent ) );
      wxCheckSetFont( dc, textFont );
    } else {
      x = rect.x;
      y = rect.y + ( rect.height - charHeight - ( descent - m_descent ) );
    }
  } else {
    x = rect.x;
    y = rect.y + ( rect.height - charHeight - ( descent - m_descent ) );
  }
  // Test for the optimized situations where all is selected, or none
  // is selected.
  // (a) All selected.
  if( selectionRange.GetStart() <= range.GetStart() && selectionRange.GetEnd() >= range.GetEnd() ) {
    DrawTabbedString( dc, textAttr, rect, stringChunk, x, y, true );
  }
  // (b) None selected.
  else if( selectionRange.GetEnd() < range.GetStart() || selectionRange.GetStart() > range.GetEnd() ) {
    // Draw all unselected
    DrawTabbedString( dc, textAttr, rect, stringChunk, x, y, false );
  } else {
    // (c) Part selected, part not
    // Let's draw unselected chunk, selected chunk, then unselected chunk.
    dc.SetBackgroundMode( wxTRANSPARENT );
    // 1. Initial unselected chunk, if any, up until start of selection.
    if( selectionRange.GetStart() > range.GetStart() && selectionRange.GetStart() <= range.GetEnd() ) {
      int r1 = range.GetStart();
      int s1 = selectionRange.GetStart() - 1;
      int fragmentLen = s1 - r1 + 1;
      if( fragmentLen < 0 ) {
        wxLogDebug( wxT( "Mid(%d, %d" ), ( int )( r1 - offset ), ( int )fragmentLen );
      }
      wxString stringFragment = str.Mid( r1 - offset, fragmentLen );
      DrawTabbedString( dc, textAttr, rect, stringFragment, x, y, false );
      #if USE_KERNING_FIX
      if( stringChunk.Find( wxT( "\t" ) ) == wxNOT_FOUND ) {
        // Compensate for kerning difference
        wxString stringFragment2( str.Mid( r1 - offset, fragmentLen + 1 ) );
        wxString stringFragment3( str.Mid( r1 - offset + fragmentLen, 1 ) );
        wxCoord w1, h1, w2, h2, w3, h3;
        dc.GetTextExtent( stringFragment,  & w1, & h1 );
        dc.GetTextExtent( stringFragment2, & w2, & h2 );
        dc.GetTextExtent( stringFragment3, & w3, & h3 );
        int kerningDiff = ( w1 + w3 ) - w2;
        x = x - kerningDiff;
      }
      #endif
    }
    // 2. Selected chunk, if any.
    if( selectionRange.GetEnd() >= range.GetStart() ) {
      int s1 = wxMax( selectionRange.GetStart(), range.GetStart() );
      int s2 = wxMin( selectionRange.GetEnd(), range.GetEnd() );
      int fragmentLen = s2 - s1 + 1;
      if( fragmentLen < 0 ) {
        wxLogDebug( wxT( "Mid(%d, %d" ), ( int )( s1 - offset ), ( int )fragmentLen );
      }
      wxString stringFragment = str.Mid( s1 - offset, fragmentLen );
      DrawTabbedString( dc, textAttr, rect, stringFragment, x, y, true );
      #if USE_KERNING_FIX
      if( stringChunk.Find( wxT( "\t" ) ) == wxNOT_FOUND ) {
        // Compensate for kerning difference
        wxString stringFragment2( str.Mid( s1 - offset, fragmentLen + 1 ) );
        wxString stringFragment3( str.Mid( s1 - offset + fragmentLen, 1 ) );
        wxCoord w1, h1, w2, h2, w3, h3;
        dc.GetTextExtent( stringFragment,  & w1, & h1 );
        dc.GetTextExtent( stringFragment2, & w2, & h2 );
        dc.GetTextExtent( stringFragment3, & w3, & h3 );
        int kerningDiff = ( w1 + w3 ) - w2;
        x = x - kerningDiff;
      }
      #endif
    }
    // 3. Remaining unselected chunk, if any
    if( selectionRange.GetEnd() < range.GetEnd() ) {
      int s2 = wxMin( selectionRange.GetEnd() + 1, range.GetEnd() );
      int r2 = range.GetEnd();
      int fragmentLen = r2 - s2 + 1;
      if( fragmentLen < 0 ) {
        wxLogDebug( wxT( "Mid(%d, %d" ), ( int )( s2 - offset ), ( int )fragmentLen );
      }
      wxString stringFragment = str.Mid( s2 - offset, fragmentLen );
      DrawTabbedString( dc, textAttr, rect, stringFragment, x, y, false );
    }
  }
  return true;
}

bool wxRichTextPlainText::DrawTabbedString( wxDC& dc, const wxTextAttrEx& attr, const wxRect& rect, wxString& str, wxCoord& x, wxCoord& y, bool selected ) {
  bool hasTabs = ( str.Find( wxT( '\t' ) ) != wxNOT_FOUND );
  wxArrayInt tabArray;
  int tabCount;
  if( hasTabs ) {
    if( attr.GetTabs().IsEmpty() ) {
      tabArray = wxRichTextParagraph::GetDefaultTabs();
    } else
    { tabArray = attr.GetTabs(); }
    tabCount = tabArray.GetCount();
    for( int i = 0; i < tabCount; ++i ) {
      int pos = tabArray[i];
      pos = ConvertTenthsMMToPixels( dc, pos );
      tabArray[i] = pos;
    }
  } else
  { tabCount = 0; }
  int nextTabPos = -1;
  int tabPos = -1;
  wxCoord w, h;
  if( selected ) {
    wxColour highlightColour( wxSystemSettings::GetColour( wxSYS_COLOUR_HIGHLIGHT ) );
    wxColour highlightTextColour( wxSystemSettings::GetColour( wxSYS_COLOUR_HIGHLIGHTTEXT ) );
    wxCheckSetBrush( dc, wxBrush( highlightColour ) );
    wxCheckSetPen( dc, wxPen( highlightColour ) );
    dc.SetTextForeground( highlightTextColour );
    dc.SetBackgroundMode( wxTRANSPARENT );
  } else {
    dc.SetTextForeground( attr.GetTextColour() );
    if( attr.HasFlag( wxTEXT_ATTR_BACKGROUND_COLOUR ) && attr.GetBackgroundColour().IsOk() ) {
      dc.SetBackgroundMode( wxSOLID );
      dc.SetTextBackground( attr.GetBackgroundColour() );
    } else
    { dc.SetBackgroundMode( wxTRANSPARENT ); }
  }
  wxCoord x_orig = GetParent()->GetPosition().x;
  while( hasTabs ) {
    // the string has a tab
    // break up the string at the Tab
    wxString stringChunk = str.BeforeFirst( wxT( '\t' ) );
    str = str.AfterFirst( wxT( '\t' ) );
    dc.GetTextExtent( stringChunk, & w, & h );
    tabPos = x + w;
    bool not_found = true;
    for( int i = 0; i < tabCount && not_found; ++i ) {
      nextTabPos = tabArray.Item( i ) + x_orig;
      // Find the next tab position.
      // Even if we're at the end of the tab array, we must still draw the chunk.
      if( nextTabPos > tabPos || ( i == ( tabCount - 1 ) ) ) {
        if( nextTabPos <= tabPos ) {
          int defaultTabWidth = ConvertTenthsMMToPixels( dc, WIDTH_FOR_DEFAULT_TABS );
          nextTabPos = tabPos + defaultTabWidth;
        }
        not_found = false;
        if( selected ) {
          w = nextTabPos - x;
          wxRect selRect( x, rect.y, w, rect.GetHeight() );
          dc.DrawRectangle( selRect );
        }
        dc.DrawText( stringChunk, x, y );
        if( attr.HasTextEffects() && ( attr.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH ) ) {
          wxPen oldPen = dc.GetPen();
          wxCheckSetPen( dc, wxPen( attr.GetTextColour(), 1 ) );
          dc.DrawLine( x, ( int )( y + ( h / 2 ) + 0.5 ), x + w, ( int )( y + ( h / 2 ) + 0.5 ) );
          wxCheckSetPen( dc, oldPen );
        }
        x = nextTabPos;
      }
    }
    hasTabs = ( str.Find( wxT( '\t' ) ) != wxNOT_FOUND );
  }
  if( !str.IsEmpty() ) {
    dc.GetTextExtent( str, & w, & h );
    if( selected ) {
      wxRect selRect( x, rect.y, w, rect.GetHeight() );
      dc.DrawRectangle( selRect );
    }
    dc.DrawText( str, x, y );
    if( attr.HasTextEffects() && ( attr.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH ) ) {
      wxPen oldPen = dc.GetPen();
      wxCheckSetPen( dc, wxPen( attr.GetTextColour(), 1 ) );
      dc.DrawLine( x, ( int )( y + ( h / 2 ) + 0.5 ), x + w, ( int )( y + ( h / 2 ) + 0.5 ) );
      wxCheckSetPen( dc, oldPen );
    }
    x += w;
  }
  return true;
}

/// Lay the item out
bool wxRichTextPlainText::Layout( wxDC& dc, const wxRect& ( rect ), int ( style ) ) {
  // Only lay out if we haven't already cached the size
  if( m_size.x == -1 ) {
    GetRangeSize( GetRange(), m_size, m_descent, dc, 0, GetParent() ? GetParent()->GetPosition() : wxPoint( 0, 0 ) );
  }
  return true;
}

/// Copy
void wxRichTextPlainText::Copy( const wxRichTextPlainText& obj ) {
  wxRichTextObject::Copy( obj );
  m_text = obj.m_text;
}

/// Get/set the object size for the given range. Returns false if the range
/// is invalid for this object.
bool wxRichTextPlainText::GetRangeSize( const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, int ( flags ), wxPoint position ) const {
  if( !range.IsWithin( GetRange() ) ) {
    return false;
  }
  wxRichTextParagraph* para = wxDynamicCast( GetParent(), wxRichTextParagraph );
  wxASSERT( para != NULL );
  int relativeX = position.x - GetParent()->GetPosition().x;
  wxTextAttrEx textAttr( para ? para->GetCombinedAttributes( GetAttributes() ) : GetAttributes() );
  // Always assume unformatted text, since at this level we have no knowledge
  // of line breaks - and we don't need it, since we'll calculate size within
  // formatted text by doing it in chunks according to the line ranges
  bool bScript( false );
  wxFont font( textAttr.GetFont() );
  if( font.Ok() ) {
    if( textAttr.HasTextEffects() && ( ( textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT )
                                       || ( textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT ) ) ) {
      wxFont textFont = font;
      double size = static_cast<double>( textFont.GetPointSize() ) / wxSCRIPT_MUL_FACTOR;
      textFont.SetPointSize( static_cast<int>( size ) );
      wxCheckSetFont( dc, textFont );
      bScript = true;
    } else
    { wxCheckSetFont( dc, font ); }
  }
  int startPos = range.GetStart() - GetRange().GetStart();
  long len = range.GetLength();
  #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
  wxArrayInt textExtents;
  #endif
  bool haveDescent = false;
  wxString str( m_text );
  wxString toReplace = wxRichTextLineBreakChar;
  str.Replace( toReplace, wxT( " " ) );
  wxString stringChunk = str.Mid( startPos, ( size_t ) len );
  if( textAttr.HasTextEffects() && ( textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_CAPITALS ) ) {
    stringChunk.MakeUpper();
  }
  wxCoord w, h;
  int width = 0;
  if( stringChunk.Find( wxT( '\t' ) ) != wxNOT_FOUND ) {
    // the string has a tab
    wxArrayInt tabArray;
    if( textAttr.GetTabs().IsEmpty() ) {
      tabArray = wxRichTextParagraph::GetDefaultTabs();
    } else
    { tabArray = textAttr.GetTabs(); }
    int tabCount = tabArray.GetCount();
    for( int i = 0; i < tabCount; ++i ) {
      int pos = tabArray[i];
      pos = ( ( wxRichTextPlainText* ) this )->ConvertTenthsMMToPixels( dc, pos );
      tabArray[i] = pos;
    }
    int nextTabPos = -1;
    while( stringChunk.Find( wxT( '\t' ) ) >= 0 ) {
      int absoluteWidth = 0;
      // the string has a tab
      // break up the string at the Tab
      wxString stringFragment = stringChunk.BeforeFirst( wxT( '\t' ) );
      stringChunk = stringChunk.AfterFirst( wxT( '\t' ) );
      #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
      if( g_UseGlobalPartialTextExtents ) {
        int oldWidth;
        if( textExtents.GetCount() > 0 ) {
          oldWidth = textExtents[textExtents.GetCount() - 1];
        } else
        { oldWidth = 0; }
        // Add these partial extents
        wxArrayInt p;
        dc.GetPartialTextExtents( stringFragment, p );
        size_t j;
        for( j = 0; j < p.GetCount(); j++ ) {
          textExtents.Add( oldWidth + p[j] );
        }
        if( textExtents.GetCount() > 0 ) {
          absoluteWidth = textExtents[textExtents.GetCount() - 1] + relativeX;
        } else
        { absoluteWidth = relativeX; }
      } else
      #endif
      {
        dc.GetTextExtent( stringFragment, & w, & h, & descent );
        width += w;
        absoluteWidth = width + position.x;
        haveDescent = true;
      }
      bool notFound = true;
      for( int i = 0; i < tabCount && notFound; ++i ) {
        nextTabPos = tabArray.Item( i );
        // Find the next tab position.
        // Even if we're at the end of the tab array, we must still process the chunk.
        if( nextTabPos > absoluteWidth || ( i == ( tabCount - 1 ) ) ) {
          if( nextTabPos <= absoluteWidth ) {
            int defaultTabWidth = ( ( wxRichTextPlainText* ) this )->ConvertTenthsMMToPixels( dc, WIDTH_FOR_DEFAULT_TABS );
            nextTabPos = absoluteWidth + defaultTabWidth;
          }
          notFound = false;
          width = nextTabPos - relativeX;
          #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
          if( g_UseGlobalPartialTextExtents ) {
            textExtents.Add( width );
          }
          #endif
        }
      }
    }
  }
  if( !stringChunk.IsEmpty() ) {
    #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
    if( g_UseGlobalPartialTextExtents ) {
      int oldWidth;
      if( textExtents.GetCount() > 0 ) {
        oldWidth = textExtents[textExtents.GetCount() - 1];
      } else
      { oldWidth = 0; }
      // Add these partial extents
      wxArrayInt p;
      dc.GetPartialTextExtents( stringChunk, p );
      size_t j;
      for( j = 0; j < p.GetCount(); j++ ) {
        textExtents.Add( oldWidth + p[j] );
      }
    } else
    #endif
    {
      dc.GetTextExtent( stringChunk, & w, & h, & descent );
      width += w;
      haveDescent = true;
    }
  }
  #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
  if( g_UseGlobalPartialTextExtents ) {
    // Now add this child's extents to the global extents
    int lastExtent = 0;
    if( g_GlobalPartialTextExtents.GetCount() > 0 ) {
      lastExtent = g_GlobalPartialTextExtents[g_GlobalPartialTextExtents.GetCount() - 1];
    }
    size_t j;
    for( j = 0; j < textExtents.GetCount(); j++ ) {
      g_GlobalPartialTextExtents.Add( lastExtent + textExtents[j] );
    }
    int charHeight = dc.GetCharHeight();
    if( textExtents.GetCount() > 0 ) {
      w = textExtents[textExtents.GetCount() - 1];
    } else
    { w = 0; }
    size = wxSize( w, charHeight );
  } else
  #endif
  {
    size = wxSize( width, dc.GetCharHeight() );
  }
  if( !haveDescent ) {
    dc.GetTextExtent( wxT( "X" ), & w, & h, & descent );
  }
  if( bScript ) {
    dc.SetFont( font );
  }
  return true;
}

/// Do a split, returning an object containing the second part, and setting
/// the first part in 'this'.
wxRichTextObject* wxRichTextPlainText::DoSplit( long pos ) {
  long index = pos - GetRange().GetStart();
  if( index < 0 || index >= ( int ) m_text.length() ) {
    return NULL;
  }
  wxString firstPart = m_text.Mid( 0, index );
  wxString secondPart = m_text.Mid( index );
  m_text = firstPart;
  wxRichTextPlainText* newObject = new wxRichTextPlainText( secondPart );
  newObject->SetAttributes( GetAttributes() );
  newObject->SetRange( wxRichTextRange( pos, GetRange().GetEnd() ) );
  GetRange().SetEnd( pos - 1 );
  return newObject;
}

/// Calculate range
void wxRichTextPlainText::CalculateRange( long start, long& end ) {
  end = start + m_text.length() - 1;
  m_range.SetRange( start, end );
}

/// Delete range
bool wxRichTextPlainText::DeleteRange( const wxRichTextRange& range ) {
  wxRichTextRange r = range;
  r.LimitTo( GetRange() );
  if( r.GetStart() == GetRange().GetStart() && r.GetEnd() == GetRange().GetEnd() ) {
    m_text.Empty();
    return true;
  }
  long startIndex = r.GetStart() - GetRange().GetStart();
  long len = r.GetLength();
  m_text = m_text.Mid( 0, startIndex ) + m_text.Mid( startIndex + len );
  return true;
}

/// Get text for the given range.
wxString wxRichTextPlainText::GetTextForRange( const wxRichTextRange& range ) const {
  wxRichTextRange r = range;
  r.LimitTo( GetRange() );
  long startIndex = r.GetStart() - GetRange().GetStart();
  long len = r.GetLength();
  return m_text.Mid( startIndex, len );
}

/// Returns true if this object can merge itself with the given one.
bool wxRichTextPlainText::CanMerge( wxRichTextObject* object ) const {
  return object->GetClassInfo() == CLASSINFO( wxRichTextPlainText ) &&
         ( m_text.empty() || ( ( GetAttributes().GetFlags() == object->GetAttributes().GetFlags() ) && wxTextAttrEqPartial( GetAttributes(), object->GetAttributes(), GetAttributes().GetFlags() ) ) );
}

/// Returns true if this object merged itself with the given one.
/// The calling code will then delete the given object.
bool wxRichTextPlainText::Merge( wxRichTextObject* object ) {
  wxRichTextPlainText* textObject = wxDynamicCast( object, wxRichTextPlainText );
  wxASSERT( textObject != NULL );
  if( textObject ) {
    bool isEmpty = m_text.IsEmpty();
    m_text += textObject->GetText();
    // Don't keep an empty object's attributes
    if( isEmpty ) {
      m_attributes = textObject->GetAttributes();
    } else
    { wxRichTextApplyStyle( m_attributes, textObject->GetAttributes() ); }
    return true;
  } else
  { return false; }
}

/// Dump to output stream for debugging
void wxRichTextPlainText::Dump( wxTextOutputStream& stream ) {
  wxRichTextObject::Dump( stream );
  stream << m_text << wxT( "\n" );
}

/// Get the first position from pos that has a line break character.
long wxRichTextPlainText::GetFirstLineBreakPosition( long pos ) {
  int i;
  int len = m_text.length();
  int startPos = pos - m_range.GetStart();
  for( i = startPos; i < len; i++ ) {
    wxChar ch = m_text[i];
    if( ch == wxRichTextLineBreakChar ) {
      return i + m_range.GetStart();
    }
  }
  return -1;
}

/*!
  wxRichTextBuffer
  This is a kind of box, used to represent the whole buffer
*/

IMPLEMENT_DYNAMIC_CLASS( wxRichTextBuffer, wxRichTextParagraphLayoutBox )

wxList                  wxRichTextBuffer::sm_handlers;
wxRichTextRenderer*     wxRichTextBuffer::sm_renderer = NULL;
int                     wxRichTextBuffer::sm_bulletRightMargin = 20;
float                   wxRichTextBuffer::sm_bulletProportion = ( float ) 0.3;

/// Initialisation
void wxRichTextBuffer::Init() {
  m_commandProcessor = new wxCommandProcessor;
  m_styleSheet = NULL;
  m_modified = false;
  m_batchedCommandDepth = 0;
  m_batchedCommand = NULL;
  m_suppressUndo = 0;
  m_handlerFlags = 0;
  m_scale = 1.0;
}

/// Initialisation
wxRichTextBuffer::~wxRichTextBuffer() {
  delete m_commandProcessor;
  delete m_batchedCommand;
  ClearStyleStack();
  ClearEventHandlers();
}

void wxRichTextBuffer::ResetAndClearCommands() {
  Reset();
  GetCommandProcessor()->ClearCommands();
  Modify( false );
  Invalidate( wxRICHTEXT_ALL );
}

void wxRichTextBuffer::Copy( const wxRichTextBuffer& obj ) {
  wxRichTextParagraphLayoutBox::Copy( obj );
  m_styleSheet = obj.m_styleSheet;
  m_modified = obj.m_modified;
  m_batchedCommandDepth = obj.m_batchedCommandDepth;
  m_batchedCommand = obj.m_batchedCommand;
  m_suppressUndo = obj.m_suppressUndo;
}

/// Push style sheet to top of stack
bool wxRichTextBuffer::PushStyleSheet( wxRichTextStyleSheet* styleSheet ) {
  if( m_styleSheet ) {
    styleSheet->InsertSheet( m_styleSheet );
  }
  SetStyleSheet( styleSheet );
  return true;
}

/// Pop style sheet from top of stack
wxRichTextStyleSheet* wxRichTextBuffer::PopStyleSheet() {
  if( m_styleSheet ) {
    wxRichTextStyleSheet* oldSheet = m_styleSheet;
    m_styleSheet = oldSheet->GetNextSheet();
    oldSheet->Unlink();
    return oldSheet;
  } else
  { return NULL; }
}

/// Submit command to insert paragraphs
bool wxRichTextBuffer::InsertParagraphsWithUndo( long pos, const wxRichTextParagraphLayoutBox& paragraphs, wxRichTextCtrl* ctrl, int ( flags ) ) {
  wxRichTextAction* action = new wxRichTextAction( NULL, _( "Insert Text" ), wxRICHTEXT_INSERT, this, ctrl, false );
  action->GetNewParagraphs() = paragraphs;
  action->SetPosition( pos );
  wxRichTextRange range = wxRichTextRange( pos, pos + paragraphs.GetRange().GetEnd() - 1 );
  if( !paragraphs.GetPartialParagraph() ) {
    range.SetEnd( range.GetEnd() + 1 );
  }
  // Set the range we'll need to delete in Undo
  action->SetRange( range );
  SubmitAction( action );
  return true;
}

/// Submit command to insert the given text
bool wxRichTextBuffer::InsertTextWithUndo( long pos, const wxString& text, wxRichTextCtrl* ctrl, int flags ) {
  wxRichTextAction* action = new wxRichTextAction( NULL, _( "Insert Text" ), wxRICHTEXT_INSERT, this, ctrl, false );
  wxTextAttrEx* p = NULL;
  wxTextAttrEx paraAttr;
  if( flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE ) {
    // Get appropriate paragraph style
    paraAttr = GetStyleForNewParagraph( pos, false, false );
    if( !paraAttr.IsDefault() ) {
      p = & paraAttr;
    }
  }
  action->GetNewParagraphs().AddParagraphs( text, p );
  int length = action->GetNewParagraphs().GetRange().GetLength();
  if( text.length() > 0 && text.Last() != wxT( '\n' ) ) {
    // Don't count the newline when undoing
    length --;
    action->GetNewParagraphs().SetPartialParagraph( true );
  } else if( text.length() > 0 && text.Last() == wxT( '\n' ) ) {
    length --;
  }
  action->SetPosition( pos );
  // Set the range we'll need to delete in Undo
  action->SetRange( wxRichTextRange( pos, pos + length - 1 ) );
  SubmitAction( action );
  return true;
}

/// Submit command to insert the given text
bool wxRichTextBuffer::InsertNewlineWithUndo( long pos, wxRichTextCtrl* ctrl, int flags ) {
  wxRichTextAction* action = new wxRichTextAction( NULL, _( "Insert Text" ), wxRICHTEXT_INSERT, this, ctrl, false );
  wxTextAttrEx* p = NULL;
  wxTextAttrEx paraAttr;
  if( flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE ) {
    paraAttr = GetStyleForNewParagraph( pos, false, true /* look for next paragraph style */ );
    if( !paraAttr.IsDefault() ) {
      p = & paraAttr;
    }
  }
  wxTextAttrEx attr( GetDefaultStyle() );
  wxRichTextParagraph* newPara = new wxRichTextParagraph( wxEmptyString, this, & attr );
  action->GetNewParagraphs().AppendChild( newPara );
  action->GetNewParagraphs().UpdateRanges();
  action->GetNewParagraphs().SetPartialParagraph( false );
  wxRichTextParagraph* para = GetParagraphAtPosition( pos, false );
  long pos1 = pos;
  if( p ) {
    newPara->SetAttributes( *p );
  }
  if( flags & wxRICHTEXT_INSERT_INTERACTIVE ) {
    if( para && para->GetRange().GetEnd() == pos ) {
      pos1 ++;
    }
    // Now see if we need to number the paragraph.
    if( newPara->GetAttributes().HasBulletNumber() ) {
      wxRichTextAttr numberingAttr;
      if( FindNextParagraphNumber( para, numberingAttr ) ) {
        wxRichTextApplyStyle( newPara->GetAttributes(), ( const wxRichTextAttr& ) numberingAttr );
      }
    }
  }
  action->SetPosition( pos );
  // Use the default character style
  if( !GetDefaultStyle().IsDefault() && newPara->GetChildren().GetFirst() ) {
    // Check whether the default style merely reflects the paragraph/basic style,
    // in which case don't apply it.
    wxTextAttrEx defaultStyle( GetDefaultStyle() );
    wxTextAttrEx toApply;
    if( para ) {
      wxRichTextAttr combinedAttr = para->GetCombinedAttributes();
      wxTextAttrEx newAttr;
      // This filters out attributes that are accounted for by the current
      // paragraph/basic style
      wxRichTextApplyStyle( toApply, defaultStyle, & combinedAttr );
    } else
    { toApply = defaultStyle; }
    if( !toApply.IsDefault() ) {
      newPara->GetChildren().GetFirst()->GetData()->SetAttributes( toApply );
    }
  }
  // Set the range we'll need to delete in Undo
  action->SetRange( wxRichTextRange( pos1, pos1 ) );
  SubmitAction( action );
  return true;
}

/// Submit command to insert the given image
bool wxRichTextBuffer::InsertImageWithUndo( long pos, const wxRichTextImageBlock& imageBlock, wxRichTextCtrl* ctrl, int flags ) {
  wxRichTextAction* action = new wxRichTextAction( NULL, _( "Insert Image" ), wxRICHTEXT_INSERT, this, ctrl, false );
  wxTextAttrEx* p = NULL;
  wxTextAttrEx paraAttr;
  if( flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE ) {
    paraAttr = GetStyleForNewParagraph( pos );
    if( !paraAttr.IsDefault() ) {
      p = & paraAttr;
    }
  }
  wxTextAttrEx attr( GetDefaultStyle() );
  wxRichTextParagraph* newPara = new wxRichTextParagraph( this, & attr );
  if( p ) {
    newPara->SetAttributes( *p );
  }
  wxRichTextImage* imageObject = new wxRichTextImage( imageBlock, newPara );
  newPara->AppendChild( imageObject );
  action->GetNewParagraphs().AppendChild( newPara );
  action->GetNewParagraphs().UpdateRanges();
  action->GetNewParagraphs().SetPartialParagraph( true );
  action->SetPosition( pos );
  // Set the range we'll need to delete in Undo
  action->SetRange( wxRichTextRange( pos, pos ) );
  SubmitAction( action );
  return true;
}

/// Get the style that is appropriate for a new paragraph at this position.
/// If the previous paragraph has a paragraph style name, look up the next-paragraph
/// style.
wxRichTextAttr wxRichTextBuffer::GetStyleForNewParagraph( long pos, bool caretPosition, bool lookUpNewParaStyle ) const {
  wxRichTextParagraph* para = GetParagraphAtPosition( pos, caretPosition );
  if( para ) {
    wxRichTextAttr attr;
    bool foundAttributes = false;
    // Look for a matching paragraph style
    if( lookUpNewParaStyle && !para->GetAttributes().GetParagraphStyleName().IsEmpty() && GetStyleSheet() ) {
      wxRichTextParagraphStyleDefinition* paraDef = GetStyleSheet()->FindParagraphStyle( para->GetAttributes().GetParagraphStyleName() );
      if( paraDef ) {
        // If we're not at the end of the paragraph, then we apply THIS style, and not the designated next style.
        if( para->GetRange().GetEnd() == pos && !paraDef->GetNextStyle().IsEmpty() ) {
          wxRichTextParagraphStyleDefinition* nextParaDef = GetStyleSheet()->FindParagraphStyle( paraDef->GetNextStyle() );
          if( nextParaDef ) {
            foundAttributes = true;
            attr = nextParaDef->GetStyleMergedWithBase( GetStyleSheet() );
          }
        }
        // If we didn't find the 'next style', use this style instead.
        if( !foundAttributes ) {
          foundAttributes = true;
          attr = paraDef->GetStyleMergedWithBase( GetStyleSheet() );
        }
      }
    }
    // Also apply list style if present
    if( lookUpNewParaStyle && !para->GetAttributes().GetListStyleName().IsEmpty() && GetStyleSheet() ) {
      wxRichTextListStyleDefinition* listDef = GetStyleSheet()->FindListStyle( para->GetAttributes().GetListStyleName() );
      if( listDef ) {
        int thisIndent = para->GetAttributes().GetLeftIndent();
        int thisLevel = para->GetAttributes().HasOutlineLevel() ? para->GetAttributes().GetOutlineLevel() : listDef->FindLevelForIndent( thisIndent );
        // Apply the overall list style, and item style for this level
        wxTextAttrEx listStyle( listDef->GetCombinedStyleForLevel( thisLevel, GetStyleSheet() ) );
        wxRichTextApplyStyle( attr, listStyle );
        attr.SetOutlineLevel( thisLevel );
        if( para->GetAttributes().HasBulletNumber() ) {
          attr.SetBulletNumber( para->GetAttributes().GetBulletNumber() );
        }
      }
    }
    if( !foundAttributes ) {
      attr = para->GetAttributes();
      int flags = attr.GetFlags();
      // Eliminate character styles
      flags &= ( ( ~ wxTEXT_ATTR_FONT ) |
                 ( ~ wxTEXT_ATTR_TEXT_COLOUR ) |
                 ( ~ wxTEXT_ATTR_BACKGROUND_COLOUR ) );
      attr.SetFlags( flags );
    }
    return attr;
  } else
  { return wxRichTextAttr(); }
}

/// Submit command to delete this range
bool wxRichTextBuffer::DeleteRangeWithUndo( const wxRichTextRange& range, wxRichTextCtrl* ctrl ) {
  wxRichTextAction* action = new wxRichTextAction( NULL, _( "Delete" ), wxRICHTEXT_DELETE, this, ctrl );
  action->SetPosition( ctrl->GetCaretPosition() );
  // Set the range to delete
  action->SetRange( range );
  // Copy the fragment that we'll need to restore in Undo
  CopyFragment( range, action->GetOldParagraphs() );
  // See if we're deleting a paragraph marker, in which case we need to
  // make a note not to copy the attributes from the 2nd paragraph to the 1st.
  if( range.GetStart() == range.GetEnd() ) {
    wxRichTextParagraph* para = GetParagraphAtPosition( range.GetStart() );
    if( para && para->GetRange().GetEnd() == range.GetEnd() ) {
      wxRichTextParagraph* nextPara = GetParagraphAtPosition( range.GetStart() + 1 );
      if( nextPara && nextPara != para ) {
        action->GetOldParagraphs().GetChildren().GetFirst()->GetData()->SetAttributes( nextPara->GetAttributes() );
        action->GetOldParagraphs().GetAttributes().SetFlags( action->GetOldParagraphs().GetAttributes().GetFlags() | wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE );
      }
    }
  }
  SubmitAction( action );
  return true;
}

/// Collapse undo/redo commands
bool wxRichTextBuffer::BeginBatchUndo( const wxString& cmdName ) {
  if( m_batchedCommandDepth == 0 ) {
    wxASSERT( m_batchedCommand == NULL );
    if( m_batchedCommand ) {
      GetCommandProcessor()->Store( m_batchedCommand );
    }
    m_batchedCommand = new wxRichTextCommand( cmdName );
  }
  m_batchedCommandDepth ++;
  return true;
}

/// Collapse undo/redo commands
bool wxRichTextBuffer::EndBatchUndo() {
  m_batchedCommandDepth --;
  wxASSERT( m_batchedCommandDepth >= 0 );
  wxASSERT( m_batchedCommand != NULL );
  if( m_batchedCommandDepth == 0 ) {
    GetCommandProcessor()->Store( m_batchedCommand );
    m_batchedCommand = NULL;
  }
  return true;
}

/// Submit immediately, or delay according to whether collapsing is on
bool wxRichTextBuffer::SubmitAction( wxRichTextAction* action ) {
  if( BatchingUndo() && m_batchedCommand && !SuppressingUndo() ) {
    wxRichTextCommand* cmd = new wxRichTextCommand( action->GetName() );
    cmd->AddAction( action );
    cmd->Do();
    cmd->GetActions().Clear();
    delete cmd;
    m_batchedCommand->AddAction( action );
  } else {
    wxRichTextCommand* cmd = new wxRichTextCommand( action->GetName() );
    cmd->AddAction( action );
    // Only store it if we're not suppressing undo.
    return GetCommandProcessor()->Submit( cmd, !SuppressingUndo() );
  }
  return true;
}

/// Begin suppressing undo/redo commands.
bool wxRichTextBuffer::BeginSuppressUndo() {
  m_suppressUndo ++;
  return true;
}

/// End suppressing undo/redo commands.
bool wxRichTextBuffer::EndSuppressUndo() {
  m_suppressUndo --;
  return true;
}

/// Begin using a style
bool wxRichTextBuffer::BeginStyle( const wxTextAttrEx& style ) {
  wxTextAttrEx newStyle( GetDefaultStyle() );
  // Save the old default style
  m_attributeStack.Append( ( wxObject* ) new wxTextAttrEx( GetDefaultStyle() ) );
  wxRichTextApplyStyle( newStyle, style );
  newStyle.SetFlags( style.GetFlags() | newStyle.GetFlags() );
  SetDefaultStyle( newStyle );
  return true;
}

/// End the style
bool wxRichTextBuffer::EndStyle() {
  if( !m_attributeStack.GetFirst() ) {
    wxLogDebug( _( "Too many EndStyle calls!" ) );
    return false;
  }
  wxList::compatibility_iterator node = m_attributeStack.GetLast();
  wxTextAttrEx* attr = ( wxTextAttrEx* )node->GetData();
  m_attributeStack.Erase( node );
  SetDefaultStyle( *attr );
  delete attr;
  return true;
}

/// End all styles
bool wxRichTextBuffer::EndAllStyles() {
  while( m_attributeStack.GetCount() != 0 ) {
    EndStyle();
  }
  return true;
}

/// Clear the style stack
void wxRichTextBuffer::ClearStyleStack() {
  for( wxList::compatibility_iterator node = m_attributeStack.GetFirst(); node; node = node->GetNext() ) {
    delete( wxTextAttrEx* ) node->GetData();
  }
  m_attributeStack.Clear();
}

/// Begin using bold
bool wxRichTextBuffer::BeginBold() {
  wxFont font( GetBasicStyle().GetFont() );
  font.SetWeight( wxBOLD );
  wxTextAttrEx attr;
  attr.SetFont( font, wxTEXT_ATTR_FONT_WEIGHT );
  return BeginStyle( attr );
}

/// Begin using italic
bool wxRichTextBuffer::BeginItalic() {
  wxFont font( GetBasicStyle().GetFont() );
  font.SetStyle( wxITALIC );
  wxTextAttrEx attr;
  attr.SetFont( font, wxTEXT_ATTR_FONT_ITALIC );
  return BeginStyle( attr );
}

/// Begin using underline
bool wxRichTextBuffer::BeginUnderline() {
  wxFont font( GetBasicStyle().GetFont() );
  font.SetUnderlined( true );
  wxTextAttrEx attr;
  attr.SetFont( font, wxTEXT_ATTR_FONT_UNDERLINE );
  return BeginStyle( attr );
}

/// Begin using point size
bool wxRichTextBuffer::BeginFontSize( int pointSize ) {
  wxFont font( GetBasicStyle().GetFont() );
  font.SetPointSize( pointSize );
  wxTextAttrEx attr;
  attr.SetFont( font, wxTEXT_ATTR_FONT_SIZE );
  return BeginStyle( attr );
}

/// Begin using this font
bool wxRichTextBuffer::BeginFont( const wxFont& font ) {
  wxTextAttrEx attr;
  attr.SetFlags( wxTEXT_ATTR_FONT );
  attr.SetFont( font );
  return BeginStyle( attr );
}

/// Begin using this colour
bool wxRichTextBuffer::BeginTextColour( const wxColour& colour ) {
  wxTextAttrEx attr;
  attr.SetFlags( wxTEXT_ATTR_TEXT_COLOUR );
  attr.SetTextColour( colour );
  return BeginStyle( attr );
}

/// Begin using alignment
bool wxRichTextBuffer::BeginAlignment( wxTextAttrAlignment alignment ) {
  wxTextAttrEx attr;
  attr.SetFlags( wxTEXT_ATTR_ALIGNMENT );
  attr.SetAlignment( alignment );
  return BeginStyle( attr );
}

/// Begin left indent
bool wxRichTextBuffer::BeginLeftIndent( int leftIndent, int leftSubIndent ) {
  wxTextAttrEx attr;
  attr.SetFlags( wxTEXT_ATTR_LEFT_INDENT );
  attr.SetLeftIndent( leftIndent, leftSubIndent );
  return BeginStyle( attr );
}

/// Begin right indent
bool wxRichTextBuffer::BeginRightIndent( int rightIndent ) {
  wxTextAttrEx attr;
  attr.SetFlags( wxTEXT_ATTR_RIGHT_INDENT );
  attr.SetRightIndent( rightIndent );
  return BeginStyle( attr );
}

/// Begin paragraph spacing
bool wxRichTextBuffer::BeginParagraphSpacing( int before, int after ) {
  long flags = 0;
  if( before != 0 ) {
    flags |= wxTEXT_ATTR_PARA_SPACING_BEFORE;
  }
  if( after != 0 ) {
    flags |= wxTEXT_ATTR_PARA_SPACING_AFTER;
  }
  wxTextAttrEx attr;
  attr.SetFlags( flags );
  attr.SetParagraphSpacingBefore( before );
  attr.SetParagraphSpacingAfter( after );
  return BeginStyle( attr );
}

/// Begin line spacing
bool wxRichTextBuffer::BeginLineSpacing( int lineSpacing ) {
  wxTextAttrEx attr;
  attr.SetFlags( wxTEXT_ATTR_LINE_SPACING );
  attr.SetLineSpacing( lineSpacing );
  return BeginStyle( attr );
}

/// Begin numbered bullet
bool wxRichTextBuffer::BeginNumberedBullet( int bulletNumber, int leftIndent, int leftSubIndent, int bulletStyle ) {
  wxTextAttrEx attr;
  attr.SetFlags( wxTEXT_ATTR_BULLET_STYLE | wxTEXT_ATTR_LEFT_INDENT );
  attr.SetBulletStyle( bulletStyle );
  attr.SetBulletNumber( bulletNumber );
  attr.SetLeftIndent( leftIndent, leftSubIndent );
  return BeginStyle( attr );
}

/// Begin symbol bullet
bool wxRichTextBuffer::BeginSymbolBullet( const wxString& symbol, int leftIndent, int leftSubIndent, int bulletStyle ) {
  wxTextAttrEx attr;
  attr.SetFlags( wxTEXT_ATTR_BULLET_STYLE | wxTEXT_ATTR_LEFT_INDENT );
  attr.SetBulletStyle( bulletStyle );
  attr.SetLeftIndent( leftIndent, leftSubIndent );
  attr.SetBulletText( symbol );
  return BeginStyle( attr );
}

/// Begin standard bullet
bool wxRichTextBuffer::BeginStandardBullet( const wxString& bulletName, int leftIndent, int leftSubIndent, int bulletStyle ) {
  wxTextAttrEx attr;
  attr.SetFlags( wxTEXT_ATTR_BULLET_STYLE | wxTEXT_ATTR_LEFT_INDENT );
  attr.SetBulletStyle( bulletStyle );
  attr.SetLeftIndent( leftIndent, leftSubIndent );
  attr.SetBulletName( bulletName );
  return BeginStyle( attr );
}

/// Begin named character style
bool wxRichTextBuffer::BeginCharacterStyle( const wxString& characterStyle ) {
  if( GetStyleSheet() ) {
    wxRichTextCharacterStyleDefinition* def = GetStyleSheet()->FindCharacterStyle( characterStyle );
    if( def ) {
      wxTextAttrEx attr = def->GetStyleMergedWithBase( GetStyleSheet() );
      return BeginStyle( attr );
    }
  }
  return false;
}

/// Begin named paragraph style
bool wxRichTextBuffer::BeginParagraphStyle( const wxString& paragraphStyle ) {
  if( GetStyleSheet() ) {
    wxRichTextParagraphStyleDefinition* def = GetStyleSheet()->FindParagraphStyle( paragraphStyle );
    if( def ) {
      wxTextAttrEx attr = def->GetStyleMergedWithBase( GetStyleSheet() );
      return BeginStyle( attr );
    }
  }
  return false;
}

/// Begin named list style
bool wxRichTextBuffer::BeginListStyle( const wxString& listStyle, int level, int number ) {
  if( GetStyleSheet() ) {
    wxRichTextListStyleDefinition* def = GetStyleSheet()->FindListStyle( listStyle );
    if( def ) {
      wxTextAttrEx attr( def->GetCombinedStyleForLevel( level ) );
      attr.SetBulletNumber( number );
      return BeginStyle( attr );
    }
  }
  return false;
}

/// Begin URL
bool wxRichTextBuffer::BeginURL( const wxString& url, const wxString& characterStyle ) {
  wxTextAttrEx attr;
  if( !characterStyle.IsEmpty() && GetStyleSheet() ) {
    wxRichTextCharacterStyleDefinition* def = GetStyleSheet()->FindCharacterStyle( characterStyle );
    if( def ) {
      attr = def->GetStyleMergedWithBase( GetStyleSheet() );
    }
  }
  attr.SetURL( url );
  return BeginStyle( attr );
}

/// Adds a handler to the end
void wxRichTextBuffer::AddHandler( wxRichTextFileHandler *handler ) {
  sm_handlers.Append( handler );
}

/// Inserts a handler at the front
void wxRichTextBuffer::InsertHandler( wxRichTextFileHandler *handler ) {
  sm_handlers.Insert( handler );
}

/// Removes a handler
bool wxRichTextBuffer::RemoveHandler( const wxString& name ) {
  wxRichTextFileHandler *handler = FindHandler( name );
  if( handler ) {
    sm_handlers.DeleteObject( handler );
    delete handler;
    return true;
  } else
  { return false; }
}

/// Finds a handler by filename or, if supplied, type
wxRichTextFileHandler *wxRichTextBuffer::FindHandlerFilenameOrType( const wxString& filename, int imageType ) {
  if( imageType != wxRICHTEXT_TYPE_ANY ) {
    return FindHandler( imageType );
  } else if( !filename.IsEmpty() ) {
    wxString path, file, ext;
    wxSplitPath( filename, & path, & file, & ext );
    return FindHandler( ext, imageType );
  } else
  { return NULL; }
}


/// Finds a handler by name
wxRichTextFileHandler* wxRichTextBuffer::FindHandler( const wxString& name ) {
  wxList::compatibility_iterator node = sm_handlers.GetFirst();
  while( node ) {
    wxRichTextFileHandler *handler = ( wxRichTextFileHandler* )node->GetData();
    if( handler->GetName().Lower() == name.Lower() ) {
      return handler;
    }
    node = node->GetNext();
  }
  return NULL;
}

/// Finds a handler by extension and type
wxRichTextFileHandler* wxRichTextBuffer::FindHandler( const wxString& extension, int type ) {
  wxList::compatibility_iterator node = sm_handlers.GetFirst();
  while( node ) {
    wxRichTextFileHandler *handler = ( wxRichTextFileHandler* )node->GetData();
    if( handler->GetExtension().Lower() == extension.Lower() &&
        ( type == wxRICHTEXT_TYPE_ANY || handler->GetType() == type ) ) {
      return handler;
    }
    node = node->GetNext();
  }
  return 0;
}

/// Finds a handler by type
wxRichTextFileHandler* wxRichTextBuffer::FindHandler( int type ) {
  wxList::compatibility_iterator node = sm_handlers.GetFirst();
  while( node ) {
    wxRichTextFileHandler *handler = ( wxRichTextFileHandler * )node->GetData();
    if( handler->GetType() == type ) {
      return handler;
    }
    node = node->GetNext();
  }
  return NULL;
}

void wxRichTextBuffer::InitStandardHandlers() {
  if( !FindHandler( wxRICHTEXT_TYPE_TEXT ) ) {
    AddHandler( new wxRichTextPlainTextHandler );
  }
}

void wxRichTextBuffer::CleanUpHandlers() {
  wxList::compatibility_iterator node = sm_handlers.GetFirst();
  while( node ) {
    wxRichTextFileHandler* handler = ( wxRichTextFileHandler* )node->GetData();
    wxList::compatibility_iterator next = node->GetNext();
    delete handler;
    node = next;
  }
  sm_handlers.Clear();
}

wxString wxRichTextBuffer::GetExtWildcard( bool combine, bool save, wxArrayInt* types ) {
  if( types ) {
    types->Clear();
  }
  wxString wildcard;
  wxList::compatibility_iterator node = GetHandlers().GetFirst();
  int count = 0;
  while( node ) {
    wxRichTextFileHandler* handler = ( wxRichTextFileHandler* ) node->GetData();
    if( handler->IsVisible() && ( ( save && handler->CanSave() ) || ( !save && handler->CanLoad() ) ) ) {
      if( combine ) {
        if( count > 0 ) {
          wildcard += wxT( ";" );
        }
        wildcard += wxT( "*." ) + handler->GetExtension();
      } else {
        if( count > 0 ) {
          wildcard += wxT( "|" );
        }
        wildcard += handler->GetName();
        wildcard += wxT( " " );
        wildcard += _( "files" );
        wildcard += wxT( " (*." );
        wildcard += handler->GetExtension();
        wildcard += wxT( ")|*." );
        wildcard += handler->GetExtension();
        if( types ) {
          types->Add( handler->GetType() );
        }
      }
      count ++;
    }
    node = node->GetNext();
  }
  if( combine ) {
    wildcard = wxT( "(" ) + wildcard + wxT( ")|" ) + wildcard;
  }
  return wildcard;
}

/// Load a file
bool wxRichTextBuffer::LoadFile( const wxString& filename, int type ) {
  wxRichTextFileHandler* handler = FindHandlerFilenameOrType( filename, type );
  if( handler ) {
    SetDefaultStyle( wxTextAttrEx() );
    handler->SetFlags( GetHandlerFlags() );
    bool success = handler->LoadFile( this, filename );
    Invalidate( wxRICHTEXT_ALL );
    return success;
  } else
  { return false; }
}

/// Save a file
bool wxRichTextBuffer::SaveFile( const wxString& filename, int type ) {
  wxRichTextFileHandler* handler = FindHandlerFilenameOrType( filename, type );
  if( handler ) {
    handler->SetFlags( GetHandlerFlags() );
    return handler->SaveFile( this, filename );
  } else
  { return false; }
}

/// Load from a stream
bool wxRichTextBuffer::LoadFile( wxInputStream& stream, int type ) {
  wxRichTextFileHandler* handler = FindHandler( type );
  if( handler ) {
    SetDefaultStyle( wxTextAttrEx() );
    handler->SetFlags( GetHandlerFlags() );
    bool success = handler->LoadFile( this, stream );
    Invalidate( wxRICHTEXT_ALL );
    return success;
  } else
  { return false; }
}

/// Save to a stream
bool wxRichTextBuffer::SaveFile( wxOutputStream& stream, int type ) {
  wxRichTextFileHandler* handler = FindHandler( type );
  if( handler ) {
    handler->SetFlags( GetHandlerFlags() );
    return handler->SaveFile( this, stream );
  } else
  { return false; }
}

bool wxRichTextBuffer::CopyToClipboard( const wxRichTextRange& range ) {
  bool success = false;
  #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
  if( !wxTheClipboard->IsOpened() && wxTheClipboard->Open() ) {
    wxTheClipboard->Clear();
    wxDataObjectComposite* compositeObject = new wxDataObjectComposite();
    {
      wxString text = GetTextForRange( range );
      compositeObject->Add( new wxTextDataObject( text ), false );
    }
    if( FindHandler( wxRICHTEXT_TYPE_XML ) ) {
      wxRichTextBuffer* richTextBuf = new wxRichTextBuffer;
      CopyFragment( range, *richTextBuf );
      compositeObject->Add( new wxRichTextBufferDataObject( richTextBuf ), true );
    }
    if( wxTheClipboard->SetData( compositeObject ) ) {
      success = true;
    }
    wxTheClipboard->Close();
  }
  #else
  wxUnusedVar( range );
  #endif
  return success;
}

/// Paste the clipboard content to the buffer
bool wxRichTextBuffer::PasteFromClipboard( long position ) {
  bool success = false;
  #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
  if( CanPasteFromClipboard() ) {
    if( wxTheClipboard->Open() ) {
      if( wxTheClipboard->IsSupported( wxDataFormat( wxRichTextBufferDataObject::GetRichTextBufferFormatId() ) ) ) {
        wxRichTextBufferDataObject data;
        wxTheClipboard->GetData( data );
        wxRichTextBuffer* richTextBuffer = data.GetRichTextBuffer();
        if( richTextBuffer ) {
          InsertParagraphsWithUndo( position + 1, *richTextBuffer, GetRichTextCtrl(), 0 );
          if( GetRichTextCtrl() ) {
            GetRichTextCtrl()->ShowPosition( position + richTextBuffer->GetRange().GetEnd() );
          }
          delete richTextBuffer;
        }
      } else if( wxTheClipboard->IsSupported( wxDF_TEXT ) || wxTheClipboard->IsSupported( wxDF_UNICODETEXT ) ) {
        wxTextDataObject data;
        wxTheClipboard->GetData( data );
        wxString text( data.GetText() );
        wxString text2 = text;
        InsertTextWithUndo( position + 1, text2, GetRichTextCtrl(), wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE );
        if( GetRichTextCtrl() ) {
          GetRichTextCtrl()->ShowPosition( position + text2.Length() );
        }
        success = true;
      } else if( wxTheClipboard->IsSupported( wxDF_BITMAP ) ) {
        wxBitmapDataObject data;
        wxTheClipboard->GetData( data );
        wxBitmap bitmap( data.GetBitmap() );
        wxImage image( bitmap.ConvertToImage() );
        wxRichTextAction* action = new wxRichTextAction( NULL, _( "Insert Image" ), wxRICHTEXT_INSERT, this, GetRichTextCtrl(), false );
        action->GetNewParagraphs().AddImage( image );
        if( action->GetNewParagraphs().GetChildCount() == 1 ) {
          action->GetNewParagraphs().SetPartialParagraph( true );
        }
        action->SetPosition( position + 1 );
        action->SetRange( wxRichTextRange( position + 1, position + 1 ) );
        SubmitAction( action );
        success = true;
      }
      wxTheClipboard->Close();
    }
  }
  #else
  wxUnusedVar( position );
  #endif
  return success;
}

bool wxRichTextBuffer::CanPasteFromClipboard() const {
  bool canPaste = false;
  #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
  if( !wxTheClipboard->IsOpened() && wxTheClipboard->Open() ) {
    if( wxTheClipboard->IsSupported( wxDF_TEXT ) || wxTheClipboard->IsSupported( wxDF_UNICODETEXT ) ||
        wxTheClipboard->IsSupported( wxDataFormat( wxRichTextBufferDataObject::GetRichTextBufferFormatId() ) ) ||
        wxTheClipboard->IsSupported( wxDF_BITMAP ) ) {
      canPaste = true;
    }
    wxTheClipboard->Close();
  }
  #endif
  return canPaste;
}

void wxRichTextBuffer::Dump() {
  wxString text;
  {
    wxStringOutputStream stream( & text );
    wxTextOutputStream textStream( stream );
    Dump( textStream );
  }
  wxLogDebug( text );
}

bool wxRichTextBuffer::AddEventHandler( wxEvtHandler* handler ) {
  m_eventHandlers.Append( handler );
  return true;
}

bool wxRichTextBuffer::RemoveEventHandler( wxEvtHandler* handler, bool deleteHandler ) {
  wxList::compatibility_iterator node = m_eventHandlers.Find( handler );
  if( node ) {
    m_eventHandlers.Erase( node );
    if( deleteHandler ) {
      delete handler;
    }
    return true;
  } else
  { return false; }
}

void wxRichTextBuffer::ClearEventHandlers() {
  m_eventHandlers.Clear();
}

bool wxRichTextBuffer::SendEvent( wxEvent& event, bool sendToAll ) {
  bool success = false;
  for( wxList::compatibility_iterator node = m_eventHandlers.GetFirst(); node; node = node->GetNext() ) {
    wxEvtHandler* handler = ( wxEvtHandler* ) node->GetData();
    if( handler->ProcessEvent( event ) ) {
      success = true;
      if( !sendToAll ) {
        return true;
      }
    }
  }
  return success;
}

/// Set style sheet and notify of the change
bool wxRichTextBuffer::SetStyleSheetAndNotify( wxRichTextStyleSheet* sheet ) {
  wxRichTextStyleSheet* oldSheet = GetStyleSheet();
  wxWindowID id = wxID_ANY;
  if( GetRichTextCtrl() ) {
    id = GetRichTextCtrl()->GetId();
  }
  wxRichTextEvent event( wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACING, id );
  event.SetEventObject( GetRichTextCtrl() );
  event.SetOldStyleSheet( oldSheet );
  event.SetNewStyleSheet( sheet );
  event.Allow();
  if( SendEvent( event ) && !event.IsAllowed() ) {
    if( sheet != oldSheet ) {
      delete sheet;
    }
    return false;
  }
  if( oldSheet && oldSheet != sheet ) {
    delete oldSheet;
  }
  SetStyleSheet( sheet );
  event.SetEventType( wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACED );
  event.SetOldStyleSheet( NULL );
  event.Allow();
  return SendEvent( event );
}

/// Set renderer, deleting old one
void wxRichTextBuffer::SetRenderer( wxRichTextRenderer* renderer ) {
  if( sm_renderer ) {
    delete sm_renderer;
  }
  sm_renderer = renderer;
}

bool wxRichTextStdRenderer::DrawStandardBullet( wxRichTextParagraph* paragraph, wxDC& dc, const wxTextAttrEx& bulletAttr, const wxRect& rect ) {
  if( bulletAttr.GetTextColour().Ok() ) {
    wxCheckSetPen( dc, wxPen( bulletAttr.GetTextColour() ) );
    wxCheckSetBrush( dc, wxBrush( bulletAttr.GetTextColour() ) );
  } else {
    wxCheckSetPen( dc, *wxBLACK_PEN );
    wxCheckSetBrush( dc, *wxBLACK_BRUSH );
  }
  wxFont font;
  if( bulletAttr.GetFont().Ok() ) {
    font = bulletAttr.GetFont();
  } else
  { font = ( *wxNORMAL_FONT ); }
  wxCheckSetFont( dc, font );
  int charHeight = dc.GetCharHeight();
  int bulletWidth = ( int )( ( ( float ) charHeight ) * wxRichTextBuffer::GetBulletProportion() );
  int bulletHeight = bulletWidth;
  int x = rect.x;
  // Calculate the top position of the character (as opposed to the whole line height)
  int y = rect.y + ( rect.height - charHeight );
  // Calculate where the bullet should be positioned
  y = y + ( charHeight + 1 ) / 2 - ( bulletHeight + 1 ) / 2;
  // The margin between a bullet and text.
  int margin = paragraph->ConvertTenthsMMToPixels( dc, wxRichTextBuffer::GetBulletRightMargin() );
  if( bulletAttr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT ) {
    x = rect.x + rect.width - bulletWidth - margin;
  } else if( bulletAttr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE ) {
    x = x + ( rect.width ) / 2 - bulletWidth / 2;
  }
  if( bulletAttr.GetBulletName() == wxT( "standard/square" ) ) {
    dc.DrawRectangle( x, y, bulletWidth, bulletHeight );
  } else if( bulletAttr.GetBulletName() == wxT( "standard/diamond" ) ) {
    wxPoint pts[5];
    pts[0].x = x;
    pts[0].y = y + bulletHeight / 2;
    pts[1].x = x + bulletWidth / 2;
    pts[1].y = y;
    pts[2].x = x + bulletWidth;
    pts[2].y = y + bulletHeight / 2;
    pts[3].x = x + bulletWidth / 2;
    pts[3].y = y + bulletHeight;
    dc.DrawPolygon( 4, pts );
  } else if( bulletAttr.GetBulletName() == wxT( "standard/triangle" ) ) {
    wxPoint pts[3];
    pts[0].x = x;
    pts[0].y = y;
    pts[1].x = x + bulletWidth;
    pts[1].y = y + bulletHeight / 2;
    pts[2].x = x;
    pts[2].y = y + bulletHeight;
    dc.DrawPolygon( 3, pts );
  } else if( bulletAttr.GetBulletName() == wxT( "standard/circle-outline" ) ) {
    wxCheckSetBrush( dc, *wxTRANSPARENT_BRUSH );
    dc.DrawEllipse( x, y, bulletWidth, bulletHeight );
  } else // "standard/circle", and catch-all
  { dc.DrawEllipse( x, y, bulletWidth, bulletHeight ); }
  return true;
}

bool wxRichTextStdRenderer::DrawTextBullet( wxRichTextParagraph* paragraph, wxDC& dc, const wxTextAttrEx& attr, const wxRect& rect, const wxString& text ) {
  if( !text.empty() ) {
    wxFont font;
    if( ( attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL ) && !attr.GetBulletFont().IsEmpty() && attr.GetFont().Ok() ) {
      font = ( *wxTheFontList->FindOrCreateFont( attr.GetFont().GetPointSize(), attr.GetFont().GetFamily(),
               attr.GetFont().GetStyle(), attr.GetFont().GetWeight(), attr.GetFont().GetUnderlined(),
               attr.GetBulletFont() ) );
    } else if( attr.GetFont().Ok() ) {
      font = attr.GetFont();
    } else
    { font = ( *wxNORMAL_FONT ); }
    wxCheckSetFont( dc, font );
    if( attr.GetTextColour().Ok() ) {
      dc.SetTextForeground( attr.GetTextColour() );
    }
    dc.SetBackgroundMode( wxTRANSPARENT );
    int charHeight = dc.GetCharHeight();
    wxCoord tw, th;
    dc.GetTextExtent( text, & tw, & th );
    int x = rect.x;
    // Calculate the top position of the character (as opposed to the whole line height)
    int y = rect.y + ( rect.height - charHeight );
    // The margin between a bullet and text.
    int margin = paragraph->ConvertTenthsMMToPixels( dc, wxRichTextBuffer::GetBulletRightMargin() );
    if( attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT ) {
      x = ( rect.x + rect.width ) - tw - margin;
    } else if( attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE ) {
      x = x + ( rect.width ) / 2 - tw / 2;
    }
    dc.DrawText( text, x, y );
    return true;
  } else
  { return false; }
}

bool wxRichTextStdRenderer::DrawBitmapBullet( wxRichTextParagraph* ( paragraph ), wxDC& ( dc ), const wxTextAttrEx& ( attr ), const wxRect& ( rect ) ) {
  // Currently unimplemented. The intention is to store bitmaps by name in a media store associated
  // with the buffer. The store will allow retrieval from memory, disk or other means.
  return false;
}

/// Enumerate the standard bullet names currently supported
bool wxRichTextStdRenderer::EnumerateStandardBulletNames( wxArrayString& bulletNames ) {
  bulletNames.Add( wxTRANSLATE( "standard/circle" ) );
  bulletNames.Add( wxTRANSLATE( "standard/circle-outline" ) );
  bulletNames.Add( wxTRANSLATE( "standard/square" ) );
  bulletNames.Add( wxTRANSLATE( "standard/diamond" ) );
  bulletNames.Add( wxTRANSLATE( "standard/triangle" ) );
  return true;
}

/*
   Module to initialise and clean up handlers
*/

class wxRichTextModule: public wxModule {
    DECLARE_DYNAMIC_CLASS( wxRichTextModule )
  public:
    wxRichTextModule() {}
    bool OnInit() {
      wxRichTextBuffer::SetRenderer( new wxRichTextStdRenderer );
      wxRichTextBuffer::InitStandardHandlers();
      wxRichTextParagraph::InitDefaultTabs();
      return true;
    }
    void OnExit() {
      wxRichTextBuffer::CleanUpHandlers();
      wxRichTextDecimalToRoman( -1 );
      wxRichTextParagraph::ClearDefaultTabs();
      wxRichTextCtrl::ClearAvailableFontNames();
      wxRichTextBuffer::SetRenderer( NULL );
    }
};

IMPLEMENT_DYNAMIC_CLASS( wxRichTextModule, wxModule )


// If the richtext lib is dynamically loaded after the app has already started
// (such as from wxPython) then the built-in module system will not init this
// module.  Provide this function to do it manually.
void wxRichTextModuleInit() {
  wxModule* module = new wxRichTextModule;
  module->Init();
  wxModule::RegisterModule( module );
}


/*!
  Commands for undo/redo

*/

wxRichTextCommand::wxRichTextCommand( const wxString& name, wxRichTextCommandId id, wxRichTextBuffer* buffer,
                                      wxRichTextCtrl* ctrl, bool ignoreFirstTime ): wxCommand( true, name ) {
  /* wxRichTextAction* action = */ new wxRichTextAction( this, name, id, buffer, ctrl, ignoreFirstTime );
}

wxRichTextCommand::wxRichTextCommand( const wxString& name ): wxCommand( true, name ) {
}

wxRichTextCommand::~wxRichTextCommand() {
  ClearActions();
}

void wxRichTextCommand::AddAction( wxRichTextAction* action ) {
  if( !m_actions.Member( action ) ) {
    m_actions.Append( action );
  }
}

bool wxRichTextCommand::Do() {
  for( wxList::compatibility_iterator node = m_actions.GetFirst(); node; node = node->GetNext() ) {
    wxRichTextAction* action = ( wxRichTextAction* ) node->GetData();
    action->Do();
  }
  return true;
}

bool wxRichTextCommand::Undo() {
  for( wxList::compatibility_iterator node = m_actions.GetLast(); node; node = node->GetPrevious() ) {
    wxRichTextAction* action = ( wxRichTextAction* ) node->GetData();
    action->Undo();
  }
  return true;
}

void wxRichTextCommand::ClearActions() {
  WX_CLEAR_LIST( wxList, m_actions );
}

/*!
  Individual action

*/

wxRichTextAction::wxRichTextAction( wxRichTextCommand* cmd, const wxString& name, wxRichTextCommandId id, wxRichTextBuffer* buffer,
                                    wxRichTextCtrl* ctrl, bool ignoreFirstTime ) {
  m_buffer = buffer;
  m_ignoreThis = ignoreFirstTime;
  m_cmdId = id;
  m_position = -1;
  m_ctrl = ctrl;
  m_name = name;
  m_newParagraphs.SetDefaultStyle( buffer->GetDefaultStyle() );
  m_newParagraphs.SetBasicStyle( buffer->GetBasicStyle() );
  if( cmd ) {
    cmd->AddAction( this );
  }
}

wxRichTextAction::~wxRichTextAction() {
}

void wxRichTextAction::CalculateRefreshOptimizations( wxArrayInt& optimizationLineCharPositions, wxArrayInt& optimizationLineYPositions ) {
  #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
  if( !m_buffer->GetDirty() && m_ctrl ) { // can only do optimisation if the buffer is already laid out correctly
    wxSize clientSize = m_ctrl->GetClientSize();
    wxPoint firstVisiblePt = m_ctrl->GetFirstVisiblePoint();
    int lastY = firstVisiblePt.y + clientSize.y;
    wxRichTextParagraph* para = m_buffer->GetParagraphAtPosition( GetRange().GetStart() );
    wxRichTextObjectList::compatibility_iterator node = m_buffer->GetChildren().Find( para );
    while( node ) {
      wxRichTextParagraph* child = ( wxRichTextParagraph* ) node->GetData();
      wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
      while( node2 ) {
        wxRichTextLine* line = node2->GetData();
        wxPoint pt = line->GetAbsolutePosition();
        wxRichTextRange range = line->GetAbsoluteRange();
        if( pt.y > lastY ) {
          node2 = wxRichTextLineList::compatibility_iterator();
          node = wxRichTextObjectList::compatibility_iterator();
        } else if( range.GetStart() > GetPosition() && pt.y >= firstVisiblePt.y ) {
          optimizationLineCharPositions.Add( range.GetStart() );
          optimizationLineYPositions.Add( pt.y );
        }
        if( node2 ) {
          node2 = node2->GetNext();
        }
      }
      if( node ) {
        node = node->GetNext();
      }
    }
  }
  #endif
}

bool g_ActionInDo = false;

bool wxRichTextAction::Do() {
  m_buffer->Modify( true );
  switch( m_cmdId ) {
    case wxRICHTEXT_INSERT: {
      // Store a list of line start character and y positions so we can figure out which area
      // we need to refresh
      wxArrayInt optimizationLineCharPositions;
      wxArrayInt optimizationLineYPositions;
      #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
      CalculateRefreshOptimizations( optimizationLineCharPositions, optimizationLineYPositions );
      #endif
      m_buffer->InsertFragment( GetRange().GetStart(), m_newParagraphs );
      m_buffer->UpdateRanges();
      m_buffer->Invalidate( wxRichTextRange( wxMax( 0, GetRange().GetStart() - 1 ), GetRange().GetEnd() ) );
      long newCaretPosition = GetPosition() + m_newParagraphs.GetRange().GetLength();
      // Character position to caret position
      newCaretPosition --;
      // Don't take into account the last newline
      if( m_newParagraphs.GetPartialParagraph() ) {
        newCaretPosition --;
      } else if( m_newParagraphs.GetChildren().GetCount() > 1 ) {
        wxRichTextObject* p = ( wxRichTextObject* ) m_newParagraphs.GetChildren().GetLast()->GetData();
        if( p->GetRange().GetLength() == 1 ) {
          newCaretPosition --;
        }
      }
      newCaretPosition = wxMin( newCaretPosition, ( m_buffer->GetRange().GetEnd() - 1 ) );
      g_ActionInDo = true;
      UpdateAppearance( newCaretPosition, true /* send update event */, & optimizationLineCharPositions, & optimizationLineYPositions );
      wxRichTextEvent cmdEvent(
        wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED,
        m_ctrl ? m_ctrl->GetId() : -1 );
      cmdEvent.SetEventObject( m_ctrl ? ( wxObject* ) m_ctrl : ( wxObject* ) m_buffer );
      cmdEvent.SetRange( GetRange() );
      cmdEvent.SetPosition( GetRange().GetStart() );
      m_buffer->SendEvent( cmdEvent );
      break;
    }
    case wxRICHTEXT_DELETE: {
      // Store a list of line start character and y positions so we can figure out which area
      // we need to refresh
      wxArrayInt optimizationLineCharPositions;
      wxArrayInt optimizationLineYPositions;
      #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
      CalculateRefreshOptimizations( optimizationLineCharPositions, optimizationLineYPositions );
      #endif
      m_buffer->DeleteRange( GetRange() );
      m_buffer->UpdateRanges();
      m_buffer->Invalidate( wxRichTextRange( GetRange().GetStart(), GetRange().GetStart() ) );
      long caretPos = GetRange().GetStart() - 1;
      if( caretPos >= m_buffer->GetRange().GetEnd() ) {
        caretPos --;
      }
      g_ActionInDo = true;
      UpdateAppearance( caretPos, true /* send update event */, & optimizationLineCharPositions, & optimizationLineYPositions );
      wxRichTextEvent cmdEvent(
        wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED,
        m_ctrl ? m_ctrl->GetId() : -1 );
      cmdEvent.SetEventObject( m_ctrl ? ( wxObject* ) m_ctrl : ( wxObject* ) m_buffer );
      cmdEvent.SetRange( GetRange() );
      cmdEvent.SetPosition( GetRange().GetStart() );
      m_buffer->SendEvent( cmdEvent );
      break;
    }
    case wxRICHTEXT_CHANGE_STYLE: {
      ApplyParagraphs( GetNewParagraphs() );
      m_buffer->Invalidate( GetRange() );
      UpdateAppearance( GetPosition() );
      wxRichTextEvent cmdEvent(
        wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED,
        m_ctrl ? m_ctrl->GetId() : -1 );
      cmdEvent.SetEventObject( m_ctrl ? ( wxObject* ) m_ctrl : ( wxObject* ) m_buffer );
      cmdEvent.SetRange( GetRange() );
      cmdEvent.SetPosition( GetRange().GetStart() );
      m_buffer->SendEvent( cmdEvent );
      break;
    }
    default:
      break;
  }
  return true;
}

bool wxRichTextAction::Undo() {
  m_buffer->Modify( true );
  switch( m_cmdId ) {
    case wxRICHTEXT_INSERT: {
      // Store a list of line start character and y positions so we can figure out which area
      // we need to refresh
      wxArrayInt optimizationLineCharPositions;
      wxArrayInt optimizationLineYPositions;
      #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
      CalculateRefreshOptimizations( optimizationLineCharPositions, optimizationLineYPositions );
      #endif
      m_buffer->DeleteRange( GetRange() );
      m_buffer->UpdateRanges();
      m_buffer->Invalidate( wxRichTextRange( GetRange().GetStart(), GetRange().GetStart() ) );
      long newCaretPosition = GetPosition() - 1;
      g_ActionInDo = false;
      UpdateAppearance( newCaretPosition, true /* send update event */, & optimizationLineCharPositions, & optimizationLineYPositions );
      wxRichTextEvent cmdEvent(
        wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED,
        m_ctrl ? m_ctrl->GetId() : -1 );
      cmdEvent.SetEventObject( m_ctrl ? ( wxObject* ) m_ctrl : ( wxObject* ) m_buffer );
      cmdEvent.SetRange( GetRange() );
      cmdEvent.SetPosition( GetRange().GetStart() );
      m_buffer->SendEvent( cmdEvent );
      break;
    }
    case wxRICHTEXT_DELETE: {
      // Store a list of line start character and y positions so we can figure out which area
      // we need to refresh
      wxArrayInt optimizationLineCharPositions;
      wxArrayInt optimizationLineYPositions;
      #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
      CalculateRefreshOptimizations( optimizationLineCharPositions, optimizationLineYPositions );
      #endif
      m_buffer->InsertFragment( GetRange().GetStart(), m_oldParagraphs );
      m_buffer->UpdateRanges();
      m_buffer->Invalidate( GetRange() );
      g_ActionInDo = false;
      UpdateAppearance( GetPosition(), true /* send update event */, & optimizationLineCharPositions, & optimizationLineYPositions );
      wxRichTextEvent cmdEvent(
        wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED,
        m_ctrl ? m_ctrl->GetId() : -1 );
      cmdEvent.SetEventObject( m_ctrl ? ( wxObject* ) m_ctrl : ( wxObject* ) m_buffer );
      cmdEvent.SetRange( GetRange() );
      cmdEvent.SetPosition( GetRange().GetStart() );
      m_buffer->SendEvent( cmdEvent );
      break;
    }
    case wxRICHTEXT_CHANGE_STYLE: {
      ApplyParagraphs( GetOldParagraphs() );
      m_buffer->Invalidate( GetRange() );
      UpdateAppearance( GetPosition() );
      wxRichTextEvent cmdEvent(
        wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED,
        m_ctrl ? m_ctrl->GetId() : -1 );
      cmdEvent.SetEventObject( m_ctrl ? ( wxObject* ) m_ctrl : ( wxObject* ) m_buffer );
      cmdEvent.SetRange( GetRange() );
      cmdEvent.SetPosition( GetRange().GetStart() );
      m_buffer->SendEvent( cmdEvent );
      break;
    }
    default:
      break;
  }
  return true;
}

/// Update the control appearance
void wxRichTextAction::UpdateAppearance( long caretPosition, bool sendUpdateEvent, wxArrayInt* optimizationLineCharPositions, wxArrayInt* optimizationLineYPositions ) {
  if( m_ctrl ) {
    m_ctrl->SetCaretPosition( caretPosition );
    if( !m_ctrl->IsFrozen() ) {
      m_ctrl->LayoutContent();
      #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
      // Find refresh rectangle if we are in a position to optimise refresh
      if( ( m_cmdId == wxRICHTEXT_INSERT || m_cmdId == wxRICHTEXT_DELETE ) && optimizationLineCharPositions ) {
        size_t i;
        wxSize clientSize = m_ctrl->GetClientSize();
        wxPoint firstVisiblePt = m_ctrl->GetFirstVisiblePoint();
        // Start/end positions
        int firstY = 0;
        int lastY = firstVisiblePt.y + clientSize.y;
        bool foundEnd = false;
        // position offset - how many characters were inserted or deleted
        int positionOffset = GetRange().GetLength();
        // Determine whether this is Do or Undo, and adjust positionOffset accordingly
        if( ( m_cmdId == wxRICHTEXT_DELETE && g_ActionInDo ) || ( m_cmdId == wxRICHTEXT_INSERT && !g_ActionInDo ) ) {
          positionOffset = - positionOffset;
        }
        // find the first line which is being drawn at the same position as it was
        // before. Since we're talking about a simple insertion, we can assume
        // that the rest of the window does not need to be redrawn.
        wxRichTextParagraph* para = m_buffer->GetParagraphAtPosition( GetPosition() );
        if( para ) {
          // Find line containing GetPosition().
          wxRichTextLine* line = NULL;
          wxRichTextLineList::compatibility_iterator node2 = para->GetLines().GetFirst();
          while( node2 ) {
            wxRichTextLine* l = node2->GetData();
            wxRichTextRange range = l->GetAbsoluteRange();
            if( range.Contains( GetRange().GetStart() - 1 ) ) {
              line = l;
              break;
            }
            node2 = node2->GetNext();
          }
          if( line ) {
            // Step back a couple of lines to where we can be sure of reformatting correctly
            wxRichTextLineList::compatibility_iterator lineNode = para->GetLines().Find( line );
            if( lineNode ) {
              lineNode = lineNode->GetPrevious();
              if( lineNode ) {
                line = ( wxRichTextLine* ) lineNode->GetData();
                lineNode = lineNode->GetPrevious();
                if( lineNode )
                { line = ( wxRichTextLine* ) lineNode->GetData(); }
              }
            }
            firstY = line->GetAbsolutePosition().y;
          }
        }
        // find the first line which is being drawn at the same position as it was
        // before. Since we're talking about a simple insertion, we can assume
        // that the rest of the window does not need to be redrawn.
        wxRichTextObjectList::compatibility_iterator node = m_buffer->GetChildren().Find( para );
        while( node ) {
          wxRichTextParagraph* child = ( wxRichTextParagraph* ) node->GetData();
          wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
          while( node2 ) {
            wxRichTextLine* line = node2->GetData();
            wxPoint pt = line->GetAbsolutePosition();
            wxRichTextRange range = line->GetAbsoluteRange();
            // we want to find the first line that is in the same position
            // as before. This will mean we're at the end of the changed text.
            if( pt.y > lastY ) { // going past the end of the window, no more info
              node2 = wxRichTextLineList::compatibility_iterator();
              node = wxRichTextObjectList::compatibility_iterator();
            }
            // Detect last line in the buffer
            else if( !node2->GetNext() && para->GetRange().Contains( m_buffer->GetRange().GetEnd() ) ) {
              // If deleting text, make sure we refresh below as well as above
              if( positionOffset >= 0 ) {
                foundEnd = true;
                lastY = pt.y + line->GetSize().y;
              }
              node2 = wxRichTextLineList::compatibility_iterator();
              node = wxRichTextObjectList::compatibility_iterator();
              break;
            } else {
              // search for this line being at the same position as before
              for( i = 0; i < optimizationLineCharPositions->GetCount(); i++ ) {
                if( ( ( *optimizationLineCharPositions )[i] + positionOffset == range.GetStart() ) &&
                    ( ( *optimizationLineYPositions )[i] == pt.y ) ) {
                  // Stop, we're now the same as we were
                  foundEnd = true;
                  lastY = pt.y;
                  node2 = wxRichTextLineList::compatibility_iterator();
                  node = wxRichTextObjectList::compatibility_iterator();
                  break;
                }
              }
            }
            if( node2 )
            { node2 = node2->GetNext(); }
          }
          if( node ) {
            node = node->GetNext();
          }
        }
        firstY = wxMax( firstVisiblePt.y, firstY );
        if( !foundEnd ) {
          lastY = firstVisiblePt.y + clientSize.y;
        }
        // Convert to device coordinates
        wxRect rect( m_ctrl->GetPhysicalPoint( wxPoint( firstVisiblePt.x, firstY ) ), wxSize( clientSize.x, lastY - firstY ) );
        m_ctrl->RefreshRect( rect, false );
      } else
      #endif
      {
        m_ctrl->Refresh( false );
      }
      #if wxRICHTEXT_USE_OWN_CARET
      m_ctrl->PositionCaret();
      #endif
      if( sendUpdateEvent ) {
        m_ctrl->SendTextUpdatedEvent();
      }
    }
  }
}

/// Replace the buffer paragraphs with the new ones.
void wxRichTextAction::ApplyParagraphs( const wxRichTextParagraphLayoutBox& fragment ) {
  wxRichTextObjectList::compatibility_iterator node = fragment.GetChildren().GetFirst();
  while( node ) {
    wxRichTextParagraph* para = wxDynamicCast( node->GetData(), wxRichTextParagraph );
    wxASSERT( para != NULL );
    wxRichTextParagraph* existingPara = m_buffer->GetParagraphAtPosition( para->GetRange().GetStart() );
    if( existingPara ) {
      wxRichTextObjectList::compatibility_iterator bufferParaNode = m_buffer->GetChildren().Find( existingPara );
      if( bufferParaNode ) {
        wxRichTextParagraph* newPara = new wxRichTextParagraph( *para );
        newPara->SetParent( m_buffer );
        bufferParaNode->SetData( newPara );
        delete existingPara;
      }
    }
    node = node->GetNext();
  }
}

bool wxRichTextRange::LimitTo( const wxRichTextRange& range ) {
  if( m_start < range.m_start ) {
    m_start = range.m_start;
  }
  if( m_end > range.m_end ) {
    m_end = range.m_end;
  }
  return true;
}

IMPLEMENT_DYNAMIC_CLASS( wxRichTextImage, wxRichTextObject )

wxRichTextImage::wxRichTextImage( const wxImage& image, wxRichTextObject* parent, wxTextAttrEx* charStyle ):
  wxRichTextObject( parent ) {
  m_image = image;
  if( charStyle ) {
    SetAttributes( *charStyle );
  }
}

wxRichTextImage::wxRichTextImage( const wxRichTextImageBlock& imageBlock, wxRichTextObject* parent, wxTextAttrEx* charStyle ):
  wxRichTextObject( parent ) {
  m_imageBlock = imageBlock;
  m_imageBlock.Load( m_image );
  if( charStyle ) {
    SetAttributes( *charStyle );
  }
}

/// Load wxImage from the block
bool wxRichTextImage::LoadFromBlock() {
  m_imageBlock.Load( m_image );
  return m_imageBlock.Ok();
}

/// Make block from the wxImage
bool wxRichTextImage::MakeBlock() {
  if( m_imageBlock.GetImageType() == wxBITMAP_TYPE_ANY || m_imageBlock.GetImageType() == -1 ) {
    m_imageBlock.SetImageType( wxBITMAP_TYPE_PNG );
  }
  m_imageBlock.MakeImageBlock( m_image, m_imageBlock.GetImageType() );
  return m_imageBlock.Ok();
}


/// Draw the item
bool wxRichTextImage::Draw( wxDC& dc, const wxRichTextRange& range, const wxRichTextRange& selectionRange, const wxRect& rect, int ( descent ), int ( style ) ) {
  if( !m_image.Ok() && m_imageBlock.Ok() ) {
    LoadFromBlock();
  }
  if( !m_image.Ok() ) {
    return false;
  }
  if( m_image.Ok() && !m_bitmap.Ok() ) {
    m_bitmap = wxBitmap( m_image );
  }
  int y = rect.y + ( rect.height - m_image.GetHeight() );
  if( m_bitmap.Ok() ) {
    dc.DrawBitmap( m_bitmap, rect.x, y, true );
  }
  if( selectionRange.Contains( range.GetStart() ) ) {
    wxCheckSetBrush( dc, *wxBLACK_BRUSH );
    wxCheckSetPen( dc, *wxBLACK_PEN );
    dc.SetLogicalFunction( wxINVERT );
    dc.DrawRectangle( rect );
    dc.SetLogicalFunction( wxCOPY );
  }
  return true;
}

/// Lay the item out
bool wxRichTextImage::Layout( wxDC& ( dc ), const wxRect& rect, int ( style ) ) {
  if( !m_image.Ok() ) {
    LoadFromBlock();
  }
  if( m_image.Ok() ) {
    SetCachedSize( wxSize( m_image.GetWidth(), m_image.GetHeight() ) );
    SetPosition( rect.GetPosition() );
  }
  return true;
}

/// Get/set the object size for the given range. Returns false if the range
/// is invalid for this object.
bool wxRichTextImage::GetRangeSize( const wxRichTextRange& range, wxSize& size, int& ( descent ), wxDC& ( dc ), int ( flags ), wxPoint ( position ) ) const {
  if( !range.IsWithin( GetRange() ) ) {
    return false;
  }
  if( !m_image.Ok() ) {
    ( ( wxRichTextImage* ) this )->LoadFromBlock();
  }
  #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
  if( g_UseGlobalPartialTextExtents ) {
    // Now add this child's extents to the global extents
    int lastExtent = 0;
    if( g_GlobalPartialTextExtents.GetCount() > 0 ) {
      lastExtent = g_GlobalPartialTextExtents[g_GlobalPartialTextExtents.GetCount() - 1];
    }
    int thisExtent;
    if( m_image.Ok() ) {
      thisExtent = lastExtent + m_image.GetWidth();
    } else
    { thisExtent = lastExtent; }
    g_GlobalPartialTextExtents.Add( thisExtent );
  }
  #endif
  if( !m_image.Ok() ) {
    return false;
  }
  size.x = m_image.GetWidth();
  size.y = m_image.GetHeight();
  return true;
}

/// Copy
void wxRichTextImage::Copy( const wxRichTextImage& obj ) {
  wxRichTextObject::Copy( obj );
  m_image = obj.m_image;
  m_imageBlock = obj.m_imageBlock;
}

/*!
  Utilities

*/

/// Compare two attribute objects
bool wxTextAttrEq( const wxTextAttrEx& attr1, const wxTextAttrEx& attr2 ) {
  return ( attr1 == attr2 );
}

bool wxTextAttrEq( const wxTextAttrEx& attr1, const wxRichTextAttr& attr2 ) {
  return (
           attr1.GetTextColour() == attr2.GetTextColour() &&
           attr1.GetBackgroundColour() == attr2.GetBackgroundColour() &&
           attr1.GetFont().GetPointSize() == attr2.GetFontSize() &&
           attr1.GetFont().GetStyle() == attr2.GetFontStyle() &&
           attr1.GetFont().GetWeight() == attr2.GetFontWeight() &&
           attr1.GetFont().GetFaceName() == attr2.GetFontFaceName() &&
           attr1.GetFont().GetUnderlined() == attr2.GetFontUnderlined() &&
           attr1.GetTextEffects() == attr2.GetTextEffects() &&
           attr1.GetTextEffectFlags() == attr2.GetTextEffectFlags() &&
           attr1.GetAlignment() == attr2.GetAlignment() &&
           attr1.GetLeftIndent() == attr2.GetLeftIndent() &&
           attr1.GetRightIndent() == attr2.GetRightIndent() &&
           attr1.GetLeftSubIndent() == attr2.GetLeftSubIndent() &&
           wxRichTextTabsEq( attr1.GetTabs(), attr2.GetTabs() ) &&
           attr1.GetLineSpacing() == attr2.GetLineSpacing() &&
           attr1.GetParagraphSpacingAfter() == attr2.GetParagraphSpacingAfter() &&
           attr1.GetParagraphSpacingBefore() == attr2.GetParagraphSpacingBefore() &&
           attr1.GetBulletStyle() == attr2.GetBulletStyle() &&
           attr1.GetBulletNumber() == attr2.GetBulletNumber() &&
           attr1.GetBulletText() == attr2.GetBulletText() &&
           attr1.GetBulletName() == attr2.GetBulletName() &&
           attr1.GetBulletFont() == attr2.GetBulletFont() &&
           attr1.GetOutlineLevel() == attr2.GetOutlineLevel() &&
           attr1.GetCharacterStyleName() == attr2.GetCharacterStyleName() &&
           attr1.GetParagraphStyleName() == attr2.GetParagraphStyleName() &&
           attr1.GetListStyleName() == attr2.GetListStyleName() &&
           attr1.HasPageBreak() == attr2.HasPageBreak() );
}

/// Compare two attribute objects, but take into account the flags
/// specifying attributes of interest.
bool wxTextAttrEqPartial( const wxTextAttrEx& attr1, const wxTextAttrEx& attr2, int flags ) {
  if( ( flags & wxTEXT_ATTR_TEXT_COLOUR ) && attr1.GetTextColour() != attr2.GetTextColour() ) {
    return false;
  }
  if( ( flags & wxTEXT_ATTR_BACKGROUND_COLOUR ) && attr1.GetBackgroundColour() != attr2.GetBackgroundColour() ) {
    return false;
  }
  if( ( flags & wxTEXT_ATTR_FONT_FACE ) && attr1.GetFont().Ok() && attr2.GetFont().Ok() &&
      attr1.GetFont().GetFaceName() != attr2.GetFont().GetFaceName() ) {
    return false;
  }
  if( ( flags & wxTEXT_ATTR_FONT_SIZE ) && attr1.GetFont().Ok() && attr2.GetFont().Ok() &&
      attr1.GetFont().GetPointSize() != attr2.GetFont().GetPointSize() ) {
    return false;
  }
  if( ( flags & wxTEXT_ATTR_FONT_WEIGHT ) && attr1.GetFont().Ok() && attr2.GetFont().Ok() &&
      attr1.GetFont().GetWeight() != attr2.GetFont().GetWeight() ) {
    return false;
  }
  if( ( flags & wxTEXT_ATTR_FONT_ITALIC ) && attr1.GetFont().Ok() && attr2.GetFont().Ok() &&
      attr1.GetFont().GetStyle() != attr2.GetFont().GetStyle() ) {
    return false;
  }
  if( ( flags & wxTEXT_ATTR_FONT_UNDERLINE ) && attr1.GetFont().Ok() && attr2.GetFont().Ok() &&
      attr1.GetFont().GetUnderlined() != attr2.GetFont().GetUnderlined() ) {
    return false;
  }
  if( ( flags & wxTEXT_ATTR_URL ) && attr1.GetURL() != attr2.GetURL() ) {
    return false;
  }
  if( ( flags & wxTEXT_ATTR_ALIGNMENT ) && attr1.GetAlignment() != attr2.GetAlignment() ) {
    return false;
  }
  if( ( flags & wxTEXT_ATTR_LEFT_INDENT ) &&
      ( ( attr1.GetLeftIndent() != attr2.GetLeftIndent() ) || ( attr1.GetLeftSubIndent() != attr2.GetLeftSubIndent() ) ) ) {
    return false;
  }
  if( ( flags & wxTEXT_ATTR_RIGHT_INDENT ) &&
      ( attr1.GetRightIndent() != attr2.GetRightIndent() ) ) {
    return false;
  }
  if( ( flags & wxTEXT_ATTR_PARA_SPACING_AFTER ) &&
      ( attr1.GetParagraphSpacingAfter() != attr2.GetParagraphSpacingAfter() ) ) {
    return false;
  }
  if( ( flags & wxTEXT_ATTR_PARA_SPACING_BEFORE ) &&
      ( attr1.GetParagraphSpacingBefore() != attr2.GetParagraphSpacingBefore() ) ) {
    return false;
  }
  if( ( flags & wxTEXT_ATTR_LINE_SPACING ) &&
      ( attr1.GetLineSpacing() != attr2.GetLineSpacing() ) ) {
    return false;
  }
  if( ( flags & wxTEXT_ATTR_CHARACTER_STYLE_NAME ) &&
      ( attr1.GetCharacterStyleName() != attr2.GetCharacterStyleName() ) ) {
    return false;
  }
  if( ( flags & wxTEXT_ATTR_PARAGRAPH_STYLE_NAME ) &&
      ( attr1.GetParagraphStyleName() != attr2.GetParagraphStyleName() ) ) {
    return false;
  }
  if( ( flags & wxTEXT_ATTR_LIST_STYLE_NAME ) &&
      ( attr1.GetListStyleName() != attr2.GetListStyleName() ) ) {
    return false;
  }
  if( ( flags & wxTEXT_ATTR_BULLET_STYLE ) &&
      ( attr1.GetBulletStyle() != attr2.GetBulletStyle() ) ) {
    return false;
  }
  if( ( flags & wxTEXT_ATTR_BULLET_NUMBER ) &&
      ( attr1.GetBulletNumber() != attr2.GetBulletNumber() ) ) {
    return false;
  }
  if( ( flags & wxTEXT_ATTR_BULLET_TEXT ) &&
      ( attr1.GetBulletText() != attr2.GetBulletText() ) &&
      ( attr1.GetBulletFont() != attr2.GetBulletFont() ) ) {
    return false;
  }
  if( ( flags & wxTEXT_ATTR_BULLET_NAME ) &&
      ( attr1.GetBulletName() != attr2.GetBulletName() ) ) {
    return false;
  }
  if( ( flags & wxTEXT_ATTR_TABS ) &&
      !wxRichTextTabsEq( attr1.GetTabs(), attr2.GetTabs() ) ) {
    return false;
  }
  if( ( flags & wxTEXT_ATTR_PAGE_BREAK ) &&
      ( attr1.HasPageBreak() != attr2.HasPageBreak() ) ) {
    return false;
  }
  if( flags & wxTEXT_ATTR_EFFECTS ) {
    if( attr1.HasTextEffects() != attr2.HasTextEffects() ) {
      return false;
    }
    if( !wxRichTextBitlistsEqPartial( attr1.GetTextEffects(), attr2.GetTextEffects(), attr2.GetTextEffectFlags() ) ) {
      return false;
    }
  }
  if( ( flags & wxTEXT_ATTR_OUTLINE_LEVEL ) &&
      ( attr1.GetOutlineLevel() != attr2.GetOutlineLevel() ) ) {
    return false;
  }
  return true;
}

bool wxTextAttrEqPartial( const wxTextAttrEx& attr1, const wxRichTextAttr& attr2, int flags ) {
  if( ( flags & wxTEXT_ATTR_TEXT_COLOUR ) && attr1.GetTextColour() != attr2.GetTextColour() ) {
    return false;
  }
  if( ( flags & wxTEXT_ATTR_BACKGROUND_COLOUR ) && attr1.GetBackgroundColour() != attr2.GetBackgroundColour() ) {
    return false;
  }
  if( ( flags & ( wxTEXT_ATTR_FONT ) ) && !attr1.GetFont().Ok() ) {
    return false;
  }
  if( ( flags & wxTEXT_ATTR_FONT_FACE ) && attr1.GetFont().Ok() &&
      attr1.GetFont().GetFaceName() != attr2.GetFontFaceName() ) {
    return false;
  }
  if( ( flags & wxTEXT_ATTR_FONT_SIZE ) && attr1.GetFont().Ok() &&
      attr1.GetFont().GetPointSize() != attr2.GetFontSize() ) {
    return false;
  }
  if( ( flags & wxTEXT_ATTR_FONT_WEIGHT ) && attr1.GetFont().Ok() &&
      attr1.GetFont().GetWeight() != attr2.GetFontWeight() ) {
    return false;
  }
  if( ( flags & wxTEXT_ATTR_FONT_ITALIC ) && attr1.GetFont().Ok() &&
      attr1.GetFont().GetStyle() != attr2.GetFontStyle() ) {
    return false;
  }
  if( ( flags & wxTEXT_ATTR_FONT_UNDERLINE ) && attr1.GetFont().Ok() &&
      attr1.GetFont().GetUnderlined() != attr2.GetFontUnderlined() ) {
    return false;
  }
  if( ( flags & wxTEXT_ATTR_URL ) && attr1.GetURL() != attr2.GetURL() ) {
    return false;
  }
  if( ( flags & wxTEXT_ATTR_ALIGNMENT ) && attr1.GetAlignment() != attr2.GetAlignment() ) {
    return false;
  }
  if( ( flags & wxTEXT_ATTR_LEFT_INDENT ) &&
      ( ( attr1.GetLeftIndent() != attr2.GetLeftIndent() ) || ( attr1.GetLeftSubIndent() != attr2.GetLeftSubIndent() ) ) ) {
    return false;
  }
  if( ( flags & wxTEXT_ATTR_RIGHT_INDENT ) &&
      ( attr1.GetRightIndent() != attr2.GetRightIndent() ) ) {
    return false;
  }
  if( ( flags & wxTEXT_ATTR_PARA_SPACING_AFTER ) &&
      ( attr1.GetParagraphSpacingAfter() != attr2.GetParagraphSpacingAfter() ) ) {
    return false;
  }
  if( ( flags & wxTEXT_ATTR_PARA_SPACING_BEFORE ) &&
      ( attr1.GetParagraphSpacingBefore() != attr2.GetParagraphSpacingBefore() ) ) {
    return false;
  }
  if( ( flags & wxTEXT_ATTR_LINE_SPACING ) &&
      ( attr1.GetLineSpacing() != attr2.GetLineSpacing() ) ) {
    return false;
  }
  if( ( flags & wxTEXT_ATTR_CHARACTER_STYLE_NAME ) &&
      ( attr1.GetCharacterStyleName() != attr2.GetCharacterStyleName() ) ) {
    return false;
  }
  if( ( flags & wxTEXT_ATTR_PARAGRAPH_STYLE_NAME ) &&
      ( attr1.GetParagraphStyleName() != attr2.GetParagraphStyleName() ) ) {
    return false;
  }
  if( ( flags & wxTEXT_ATTR_LIST_STYLE_NAME ) &&
      ( attr1.GetListStyleName() != attr2.GetListStyleName() ) ) {
    return false;
  }
  if( ( flags & wxTEXT_ATTR_BULLET_STYLE ) &&
      ( attr1.GetBulletStyle() != attr2.GetBulletStyle() ) ) {
    return false;
  }
  if( ( flags & wxTEXT_ATTR_BULLET_NUMBER ) &&
      ( attr1.GetBulletNumber() != attr2.GetBulletNumber() ) ) {
    return false;
  }
  if( ( flags & wxTEXT_ATTR_BULLET_TEXT ) &&
      ( attr1.GetBulletText() != attr2.GetBulletText() ) &&
      ( attr1.GetBulletFont() != attr2.GetBulletFont() ) ) {
    return false;
  }
  if( ( flags & wxTEXT_ATTR_BULLET_NAME ) &&
      ( attr1.GetBulletName() != attr2.GetBulletName() ) ) {
    return false;
  }
  if( ( flags & wxTEXT_ATTR_TABS ) &&
      !wxRichTextTabsEq( attr1.GetTabs(), attr2.GetTabs() ) ) {
    return false;
  }
  if( ( flags & wxTEXT_ATTR_PAGE_BREAK ) &&
      ( attr1.HasPageBreak() != attr2.HasPageBreak() ) ) {
    return false;
  }
  if( flags & wxTEXT_ATTR_EFFECTS ) {
    if( attr1.HasTextEffects() != attr2.HasTextEffects() ) {
      return false;
    }
    if( !wxRichTextBitlistsEqPartial( attr1.GetTextEffects(), attr2.GetTextEffects(), attr2.GetTextEffectFlags() ) ) {
      return false;
    }
  }
  if( ( flags & wxTEXT_ATTR_OUTLINE_LEVEL ) &&
      ( attr1.GetOutlineLevel() != attr2.GetOutlineLevel() ) ) {
    return false;
  }
  return true;
}

/// Compare tabs
bool wxRichTextTabsEq( const wxArrayInt& tabs1, const wxArrayInt& tabs2 ) {
  if( tabs1.GetCount() != tabs2.GetCount() ) {
    return false;
  }
  size_t i;
  for( i = 0; i < tabs1.GetCount(); i++ ) {
    if( tabs1[i] != tabs2[i] ) {
      return false;
    }
  }
  return true;
}

bool wxResetIncompatibleBits( int mask, const int srcFlags, int& destFlags, int& destBits ) {
  if( ( srcFlags & mask ) && ( destFlags & mask ) ) {
    destBits &= ~mask;
    destFlags &= ~mask;
  }
  return true;
}

/// Apply one style to another
bool wxRichTextApplyStyle( wxTextAttrEx& destStyle, const wxTextAttrEx& style ) {
  // Whole font
  if( style.GetFont().Ok() && ( ( style.GetFlags() & ( wxTEXT_ATTR_FONT ) ) == ( wxTEXT_ATTR_FONT ) ) ) {
    destStyle.SetFont( style.GetFont() );
  } else if( style.GetFont().Ok() && !destStyle.GetFont().Ok() ) {
    long oldFlags = destStyle.GetFlags();
    destStyle.SetFont( style.GetFont() );
    destStyle.SetFlags( oldFlags | ( style.GetFlags() & wxTEXT_ATTR_FONT ) );
  } else if( style.GetFont().Ok() && destStyle.GetFont().Ok() ) {
    int fontSize = destStyle.GetFont().GetPointSize();
    int fontFamily = destStyle.GetFont().GetFamily();
    int fontStyle = destStyle.GetFont().GetStyle();
    int fontWeight = destStyle.GetFont().GetWeight();
    bool fontUnderlined = destStyle.GetFont().GetUnderlined();
    wxString fontFaceName = destStyle.GetFont().GetFaceName();
    if( style.GetFlags() & wxTEXT_ATTR_FONT_FACE ) {
      destStyle.SetFlags( destStyle.GetFlags() | wxTEXT_ATTR_FONT_FACE );
      fontFaceName = style.GetFont().GetFaceName();
    }
    if( style.GetFlags() & wxTEXT_ATTR_FONT_SIZE ) {
      destStyle.SetFlags( destStyle.GetFlags() | wxTEXT_ATTR_FONT_SIZE );
      fontSize = style.GetFont().GetPointSize();
    }
    if( style.GetFlags() & wxTEXT_ATTR_FONT_ITALIC ) {
      destStyle.SetFlags( destStyle.GetFlags() | wxTEXT_ATTR_FONT_ITALIC );
      fontStyle = style.GetFont().GetStyle();
    }
    if( style.GetFlags() & wxTEXT_ATTR_FONT_WEIGHT ) {
      destStyle.SetFlags( destStyle.GetFlags() | wxTEXT_ATTR_FONT_WEIGHT );
      fontWeight = style.GetFont().GetWeight();
    }
    if( style.GetFlags() & wxTEXT_ATTR_FONT_UNDERLINE ) {
      destStyle.SetFlags( destStyle.GetFlags() | wxTEXT_ATTR_FONT_UNDERLINE );
      fontUnderlined = style.GetFont().GetUnderlined();
    }
    int oldFlags = destStyle.GetFlags();
    destStyle.SetFont( wxFont( fontSize, fontFamily, fontStyle, fontWeight, fontUnderlined, fontFaceName ) );
    destStyle.SetFlags( oldFlags );
  }
  if( style.GetTextColour().Ok() && style.HasTextColour() ) {
    destStyle.SetTextColour( style.GetTextColour() );
  }
  if( style.GetBackgroundColour().Ok() && style.HasBackgroundColour() ) {
    destStyle.SetBackgroundColour( style.GetBackgroundColour() );
  }
  if( style.HasAlignment() ) {
    destStyle.SetAlignment( style.GetAlignment() );
  }
  if( style.HasTabs() ) {
    destStyle.SetTabs( style.GetTabs() );
  }
  if( style.HasLeftIndent() ) {
    destStyle.SetLeftIndent( style.GetLeftIndent(), style.GetLeftSubIndent() );
  }
  if( style.HasRightIndent() ) {
    destStyle.SetRightIndent( style.GetRightIndent() );
  }
  if( style.HasParagraphSpacingAfter() ) {
    destStyle.SetParagraphSpacingAfter( style.GetParagraphSpacingAfter() );
  }
  if( style.HasParagraphSpacingBefore() ) {
    destStyle.SetParagraphSpacingBefore( style.GetParagraphSpacingBefore() );
  }
  if( style.HasLineSpacing() ) {
    destStyle.SetLineSpacing( style.GetLineSpacing() );
  }
  if( style.HasCharacterStyleName() ) {
    destStyle.SetCharacterStyleName( style.GetCharacterStyleName() );
  }
  if( style.HasParagraphStyleName() ) {
    destStyle.SetParagraphStyleName( style.GetParagraphStyleName() );
  }
  if( style.HasListStyleName() ) {
    destStyle.SetListStyleName( style.GetListStyleName() );
  }
  if( style.HasBulletStyle() ) {
    destStyle.SetBulletStyle( style.GetBulletStyle() );
  }
  if( style.HasBulletText() ) {
    destStyle.SetBulletText( style.GetBulletText() );
    destStyle.SetBulletFont( style.GetBulletFont() );
  }
  if( style.HasBulletName() ) {
    destStyle.SetBulletName( style.GetBulletName() );
  }
  if( style.HasBulletNumber() ) {
    destStyle.SetBulletNumber( style.GetBulletNumber() );
  }
  if( style.HasURL() ) {
    destStyle.SetURL( style.GetURL() );
  }
  if( style.HasPageBreak() ) {
    destStyle.SetPageBreak();
  }
  if( style.HasTextEffects() ) {
    int destBits = destStyle.GetTextEffects();
    int destFlags = destStyle.GetTextEffectFlags();
    int srcBits = style.GetTextEffects();
    int srcFlags = style.GetTextEffectFlags();
    // Reset incompatible bits in the destination
    wxResetIncompatibleBits( ( wxTEXT_ATTR_EFFECT_SUPERSCRIPT | wxTEXT_ATTR_EFFECT_SUBSCRIPT ), srcFlags, destFlags, destBits );
    wxResetIncompatibleBits( ( wxTEXT_ATTR_EFFECT_CAPITALS | wxTEXT_ATTR_EFFECT_SMALL_CAPITALS ), srcFlags, destFlags, destBits );
    wxResetIncompatibleBits( ( wxTEXT_ATTR_EFFECT_STRIKETHROUGH | wxTEXT_ATTR_EFFECT_DOUBLE_STRIKETHROUGH ), srcFlags, destFlags, destBits );
    wxRichTextCombineBitlists( destBits, srcBits, destFlags, srcFlags );
    destStyle.SetTextEffects( destBits );
    destStyle.SetTextEffectFlags( destFlags );
  }
  if( style.HasOutlineLevel() ) {
    destStyle.SetOutlineLevel( style.GetOutlineLevel() );
  }
  return true;
}

bool wxRichTextApplyStyle( wxRichTextAttr& destStyle, const wxTextAttrEx& style ) {
  wxTextAttrEx destStyle2 = destStyle;
  wxRichTextApplyStyle( destStyle2, style );
  destStyle = destStyle2;
  return true;
}

bool wxRichTextApplyStyle( wxRichTextAttr& destStyle, const wxRichTextAttr& style, wxRichTextAttr* compareWith ) {
  destStyle = destStyle.Combine( style, compareWith );
  return true;
}

bool wxRichTextApplyStyle( wxTextAttrEx& destStyle, const wxRichTextAttr& style, wxRichTextAttr* compareWith ) {
  // Whole font. Avoiding setting individual attributes if possible, since
  // it recreates the font each time.
  if( ( ( style.GetFlags() & ( wxTEXT_ATTR_FONT ) ) == ( wxTEXT_ATTR_FONT ) ) && !compareWith ) {
    destStyle.SetFont( wxFont( style.GetFontSize(), destStyle.GetFont().Ok() ? destStyle.GetFont().GetFamily() : wxDEFAULT,
                               style.GetFontStyle(), style.GetFontWeight(), style.GetFontUnderlined(), style.GetFontFaceName() ) );
  } else if( style.GetFlags() & ( wxTEXT_ATTR_FONT ) ) {
    int fontSize = 12;
    int fontFamily = wxDEFAULT;
    int fontStyle = wxNORMAL;
    int fontWeight = wxNORMAL;
    bool fontUnderlined = false;
    wxString fontFaceName;
    if( destStyle.GetFont().Ok() ) {
      fontSize = destStyle.GetFont().GetPointSize();
      fontFamily = destStyle.GetFont().GetFamily();
      fontStyle = destStyle.GetFont().GetStyle();
      fontWeight = destStyle.GetFont().GetWeight();
      fontUnderlined = destStyle.GetFont().GetUnderlined();
      fontFaceName = destStyle.GetFont().GetFaceName();
    }
    if( style.GetFlags() & wxTEXT_ATTR_FONT_FACE ) {
      if( compareWith && compareWith->HasFontFaceName() && compareWith->GetFontFaceName() == style.GetFontFaceName() ) {
        // The same as currently displayed, so don't set
      } else {
        destStyle.SetFlags( destStyle.GetFlags() | wxTEXT_ATTR_FONT_FACE );
        fontFaceName = style.GetFontFaceName();
      }
    }
    if( style.GetFlags() & wxTEXT_ATTR_FONT_SIZE ) {
      if( compareWith && compareWith->HasFontSize() && compareWith->GetFontSize() == style.GetFontSize() ) {
        // The same as currently displayed, so don't set
      } else {
        destStyle.SetFlags( destStyle.GetFlags() | wxTEXT_ATTR_FONT_SIZE );
        fontSize = style.GetFontSize();
      }
    }
    if( style.GetFlags() & wxTEXT_ATTR_FONT_ITALIC ) {
      if( compareWith && compareWith->HasFontItalic() && compareWith->GetFontStyle() == style.GetFontStyle() ) {
        // The same as currently displayed, so don't set
      } else {
        destStyle.SetFlags( destStyle.GetFlags() | wxTEXT_ATTR_FONT_ITALIC );
        fontStyle = style.GetFontStyle();
      }
    }
    if( style.GetFlags() & wxTEXT_ATTR_FONT_WEIGHT ) {
      if( compareWith && compareWith->HasFontWeight() && compareWith->GetFontWeight() == style.GetFontWeight() ) {
        // The same as currently displayed, so don't set
      } else {
        destStyle.SetFlags( destStyle.GetFlags() | wxTEXT_ATTR_FONT_WEIGHT );
        fontWeight = style.GetFontWeight();
      }
    }
    if( style.GetFlags() & wxTEXT_ATTR_FONT_UNDERLINE ) {
      if( compareWith && compareWith->HasFontUnderlined() && compareWith->GetFontUnderlined() == style.GetFontUnderlined() ) {
        // The same as currently displayed, so don't set
      } else {
        destStyle.SetFlags( destStyle.GetFlags() | wxTEXT_ATTR_FONT_UNDERLINE );
        fontUnderlined = style.GetFontUnderlined();
      }
    }
    wxFont font( fontSize, fontFamily, fontStyle, fontWeight, fontUnderlined, fontFaceName );
    if( font != destStyle.GetFont() ) {
      int oldFlags = destStyle.GetFlags();
      destStyle.SetFont( font );
      destStyle.SetFlags( oldFlags );
    }
  }
  if( style.GetTextColour().Ok() && style.HasTextColour() ) {
    if( !( compareWith && compareWith->HasTextColour() && compareWith->GetTextColour() == style.GetTextColour() ) ) {
      destStyle.SetTextColour( style.GetTextColour() );
    }
  }
  if( style.GetBackgroundColour().Ok() && style.HasBackgroundColour() ) {
    if( !( compareWith && compareWith->HasBackgroundColour() && compareWith->GetBackgroundColour() == style.GetBackgroundColour() ) ) {
      destStyle.SetBackgroundColour( style.GetBackgroundColour() );
    }
  }
  if( style.HasAlignment() ) {
    if( !( compareWith && compareWith->HasAlignment() && compareWith->GetAlignment() == style.GetAlignment() ) ) {
      destStyle.SetAlignment( style.GetAlignment() );
    }
  }
  if( style.HasTabs() ) {
    if( !( compareWith && compareWith->HasTabs() && wxRichTextTabsEq( compareWith->GetTabs(), style.GetTabs() ) ) ) {
      destStyle.SetTabs( style.GetTabs() );
    }
  }
  if( style.HasLeftIndent() ) {
    if( !( compareWith && compareWith->HasLeftIndent() && compareWith->GetLeftIndent() == style.GetLeftIndent()
           && compareWith->GetLeftSubIndent() == style.GetLeftSubIndent() ) ) {
      destStyle.SetLeftIndent( style.GetLeftIndent(), style.GetLeftSubIndent() );
    }
  }
  if( style.HasRightIndent() ) {
    if( !( compareWith && compareWith->HasRightIndent() && compareWith->GetRightIndent() == style.GetRightIndent() ) ) {
      destStyle.SetRightIndent( style.GetRightIndent() );
    }
  }
  if( style.HasParagraphSpacingAfter() ) {
    if( !( compareWith && compareWith->HasParagraphSpacingAfter() && compareWith->GetParagraphSpacingAfter() == style.GetParagraphSpacingAfter() ) ) {
      destStyle.SetParagraphSpacingAfter( style.GetParagraphSpacingAfter() );
    }
  }
  if( style.HasParagraphSpacingBefore() ) {
    if( !( compareWith && compareWith->HasParagraphSpacingBefore() && compareWith->GetParagraphSpacingBefore() == style.GetParagraphSpacingBefore() ) ) {
      destStyle.SetParagraphSpacingBefore( style.GetParagraphSpacingBefore() );
    }
  }
  if( style.HasLineSpacing() ) {
    if( !( compareWith && compareWith->HasLineSpacing() && compareWith->GetLineSpacing() == style.GetLineSpacing() ) ) {
      destStyle.SetLineSpacing( style.GetLineSpacing() );
    }
  }
  if( style.HasCharacterStyleName() ) {
    if( !( compareWith && compareWith->HasCharacterStyleName() && compareWith->GetCharacterStyleName() == style.GetCharacterStyleName() ) ) {
      destStyle.SetCharacterStyleName( style.GetCharacterStyleName() );
    }
  }
  if( style.HasParagraphStyleName() ) {
    if( !( compareWith && compareWith->HasParagraphStyleName() && compareWith->GetParagraphStyleName() == style.GetParagraphStyleName() ) ) {
      destStyle.SetParagraphStyleName( style.GetParagraphStyleName() );
    }
  }
  if( style.HasListStyleName() ) {
    if( !( compareWith && compareWith->HasListStyleName() && compareWith->GetListStyleName() == style.GetListStyleName() ) ) {
      destStyle.SetListStyleName( style.GetListStyleName() );
    }
  }
  if( style.HasBulletStyle() ) {
    if( !( compareWith && compareWith->HasBulletStyle() && compareWith->GetBulletStyle() == style.GetBulletStyle() ) ) {
      destStyle.SetBulletStyle( style.GetBulletStyle() );
    }
  }
  if( style.HasBulletText() ) {
    if( !( compareWith && compareWith->HasBulletText() && compareWith->GetBulletText() == style.GetBulletText() ) ) {
      destStyle.SetBulletText( style.GetBulletText() );
      destStyle.SetBulletFont( style.GetBulletFont() );
    }
  }
  if( style.HasBulletNumber() ) {
    if( !( compareWith && compareWith->HasBulletNumber() && compareWith->GetBulletNumber() == style.GetBulletNumber() ) ) {
      destStyle.SetBulletNumber( style.GetBulletNumber() );
    }
  }
  if( style.HasBulletName() ) {
    if( !( compareWith && compareWith->HasBulletName() && compareWith->GetBulletName() == style.GetBulletName() ) ) {
      destStyle.SetBulletName( style.GetBulletName() );
    }
  }
  if( style.HasURL() ) {
    if( !( compareWith && compareWith->HasURL() && compareWith->GetURL() == style.GetURL() ) ) {
      destStyle.SetURL( style.GetURL() );
    }
  }
  if( style.HasPageBreak() ) {
    if( !( compareWith && compareWith->HasPageBreak() ) ) {
      destStyle.SetPageBreak();
    }
  }
  if( style.HasTextEffects() ) {
    if( !( compareWith && compareWith->HasTextEffects() && compareWith->GetTextEffects() == style.GetTextEffects() ) ) {
      int destBits = destStyle.GetTextEffects();
      int destFlags = destStyle.GetTextEffectFlags();
      int srcBits = style.GetTextEffects();
      int srcFlags = style.GetTextEffectFlags();
      // Reset incompatible bits in the destination
      wxResetIncompatibleBits( ( wxTEXT_ATTR_EFFECT_SUPERSCRIPT | wxTEXT_ATTR_EFFECT_SUBSCRIPT ), srcFlags, destFlags, destBits );
      wxResetIncompatibleBits( ( wxTEXT_ATTR_EFFECT_CAPITALS | wxTEXT_ATTR_EFFECT_SMALL_CAPITALS ), srcFlags, destFlags, destBits );
      wxResetIncompatibleBits( ( wxTEXT_ATTR_EFFECT_STRIKETHROUGH | wxTEXT_ATTR_EFFECT_DOUBLE_STRIKETHROUGH ), srcFlags, destFlags, destBits );
      wxRichTextCombineBitlists( destBits, srcBits, destFlags, srcFlags );
      destStyle.SetTextEffects( destBits );
      destStyle.SetTextEffectFlags( destFlags );
    }
  }
  if( style.HasOutlineLevel() ) {
    if( !( compareWith && compareWith->HasOutlineLevel() && compareWith->GetOutlineLevel() == style.GetOutlineLevel() ) ) {
      destStyle.SetOutlineLevel( style.GetOutlineLevel() );
    }
  }
  return true;
}

// Remove attributes
bool wxRichTextRemoveStyle( wxTextAttrEx& destStyle, const wxRichTextAttr& style ) {
  int flags = style.GetFlags();
  int destFlags = destStyle.GetFlags();
  destStyle.SetFlags( destFlags & ~flags );
  return true;
}

/// Combine two bitlists, specifying the bits of interest with separate flags.
bool wxRichTextCombineBitlists( int& valueA, int valueB, int& flagsA, int flagsB ) {
  // We want to apply B's bits to A, taking into account each's flags which indicate which bits
  // are to be taken into account. A zero in B's bits should reset that bit in A but only if B's flags
  // indicate it.
  // First, reset the 0 bits from B. We make a mask so we're only dealing with B's zero
  // bits at this point, ignoring any 1 bits in B or 0 bits in B that are not relevant.
  int valueA2 = ~( ~valueB & flagsB ) & valueA;
  // Now combine the 1 bits.
  int valueA3 = ( valueB & flagsB ) | valueA2;
  valueA = valueA3;
  flagsA = ( flagsA | flagsB );
  return true;
}

/// Compare two bitlists
bool wxRichTextBitlistsEqPartial( int valueA, int valueB, int flags ) {
  int relevantBitsA = valueA & flags;
  int relevantBitsB = valueB & flags;
  return ( relevantBitsA != relevantBitsB );
}

/// Split into paragraph and character styles
bool wxRichTextSplitParaCharStyles( const wxTextAttrEx& style, wxTextAttrEx& parStyle, wxTextAttrEx& charStyle ) {
  wxTextAttrEx defaultCharStyle1( style );
  wxTextAttrEx defaultParaStyle1( style );
  defaultCharStyle1.SetFlags( defaultCharStyle1.GetFlags()&wxTEXT_ATTR_CHARACTER );
  defaultParaStyle1.SetFlags( defaultParaStyle1.GetFlags()&wxTEXT_ATTR_PARAGRAPH );
  wxRichTextApplyStyle( charStyle, defaultCharStyle1 );
  wxRichTextApplyStyle( parStyle, defaultParaStyle1 );
  return true;
}

void wxSetFontPreservingStyles( wxTextAttr& attr, const wxFont& font ) {
  long flags = attr.GetFlags();
  attr.SetFont( font );
  attr.SetFlags( flags );
}

/// Convert a decimal to Roman numerals
wxString wxRichTextDecimalToRoman( long n ) {
  static wxArrayInt decimalNumbers;
  static wxArrayString romanNumbers;
  // Clean up arrays
  if( n == -1 ) {
    decimalNumbers.Clear();
    romanNumbers.Clear();
    return wxEmptyString;
  }
  if( decimalNumbers.GetCount() == 0 ) {
#define wxRichTextAddDecRom(n, r) decimalNumbers.Add(n); romanNumbers.Add(r);
    wxRichTextAddDecRom( 1000, wxT( "M" ) );
    wxRichTextAddDecRom( 900, wxT( "CM" ) );
    wxRichTextAddDecRom( 500, wxT( "D" ) );
    wxRichTextAddDecRom( 400, wxT( "CD" ) );
    wxRichTextAddDecRom( 100, wxT( "C" ) );
    wxRichTextAddDecRom( 90, wxT( "XC" ) );
    wxRichTextAddDecRom( 50, wxT( "L" ) );
    wxRichTextAddDecRom( 40, wxT( "XL" ) );
    wxRichTextAddDecRom( 10, wxT( "X" ) );
    wxRichTextAddDecRom( 9, wxT( "IX" ) );
    wxRichTextAddDecRom( 5, wxT( "V" ) );
    wxRichTextAddDecRom( 4, wxT( "IV" ) );
    wxRichTextAddDecRom( 1, wxT( "I" ) );
  }
  int i = 0;
  wxString roman;
  while( n > 0 && i < 13 ) {
    if( n >= decimalNumbers[i] ) {
      n -= decimalNumbers[i];
      roman += romanNumbers[i];
    } else
    { i ++; }
  }
  if( roman.IsEmpty() ) {
    roman = wxT( "0" );
  }
  return roman;
}

/*!
  wxRichTextAttr stores attributes without a wxFont object, so is a much more
  efficient way to query styles.
*/

// ctors
wxRichTextAttr::wxRichTextAttr( const wxColour& colText,
                                const wxColour& colBack,
                                wxTextAttrAlignment alignment ): m_textAlignment( alignment ), m_colText( colText ), m_colBack( colBack ) {
  Init();
  if( m_colText.Ok() ) {
    m_flags |= wxTEXT_ATTR_TEXT_COLOUR;
  }
  if( m_colBack.Ok() ) {
    m_flags |= wxTEXT_ATTR_BACKGROUND_COLOUR;
  }
  if( alignment != wxTEXT_ALIGNMENT_DEFAULT ) {
    m_flags |= wxTEXT_ATTR_ALIGNMENT;
  }
}

wxRichTextAttr::wxRichTextAttr( const wxTextAttrEx& attr ) {
  Init();
  ( *this ) = attr;
}

wxRichTextAttr::wxRichTextAttr( const wxRichTextAttr& attr ) {
  Copy( attr );
}

// operations
void wxRichTextAttr::Init() {
  m_textAlignment = wxTEXT_ALIGNMENT_DEFAULT;
  m_flags = 0;
  m_leftIndent = 0;
  m_leftSubIndent = 0;
  m_rightIndent = 0;
  m_fontSize = 12;
  m_fontStyle = wxNORMAL;
  m_fontWeight = wxNORMAL;
  m_fontUnderlined = false;
  m_paragraphSpacingAfter = 0;
  m_paragraphSpacingBefore = 0;
  m_lineSpacing = 0;
  m_bulletStyle = wxTEXT_ATTR_BULLET_STYLE_NONE;
  m_textEffects = wxTEXT_ATTR_EFFECT_NONE;
  m_textEffectFlags = wxTEXT_ATTR_EFFECT_NONE;
  m_outlineLevel = 0;
  m_bulletNumber = 0;
}

// Copy
void wxRichTextAttr::Copy( const wxRichTextAttr& attr ) {
  m_colText = attr.m_colText;
  m_colBack = attr.m_colBack;
  m_textAlignment = attr.m_textAlignment;
  m_leftIndent = attr.m_leftIndent;
  m_leftSubIndent = attr.m_leftSubIndent;
  m_rightIndent = attr.m_rightIndent;
  m_tabs = attr.m_tabs;
  m_flags = attr.m_flags;
  m_fontSize = attr.m_fontSize;
  m_fontStyle = attr.m_fontStyle;
  m_fontWeight = attr.m_fontWeight;
  m_fontUnderlined = attr.m_fontUnderlined;
  m_fontFaceName = attr.m_fontFaceName;
  m_textEffects = attr.m_textEffects;
  m_textEffectFlags = attr.m_textEffectFlags;
  m_paragraphSpacingAfter = attr.m_paragraphSpacingAfter;
  m_paragraphSpacingBefore = attr.m_paragraphSpacingBefore;
  m_lineSpacing = attr.m_lineSpacing;
  m_characterStyleName = attr.m_characterStyleName;
  m_paragraphStyleName = attr.m_paragraphStyleName;
  m_listStyleName = attr.m_listStyleName;
  m_bulletStyle = attr.m_bulletStyle;
  m_bulletNumber = attr.m_bulletNumber;
  m_bulletText = attr.m_bulletText;
  m_bulletFont = attr.m_bulletFont;
  m_bulletName = attr.m_bulletName;
  m_outlineLevel = attr.m_outlineLevel;
  m_urlTarget = attr.m_urlTarget;
}

// operators
void wxRichTextAttr::operator= ( const wxRichTextAttr& attr ) {
  Copy( attr );
}

// operators
void wxRichTextAttr::operator= ( const wxTextAttrEx& attr ) {
  m_flags = attr.GetFlags();
  m_colText = attr.GetTextColour();
  m_colBack = attr.GetBackgroundColour();
  m_textAlignment = attr.GetAlignment();
  m_leftIndent = attr.GetLeftIndent();
  m_leftSubIndent = attr.GetLeftSubIndent();
  m_rightIndent = attr.GetRightIndent();
  m_tabs = attr.GetTabs();
  m_textEffects = attr.GetTextEffects();
  m_textEffectFlags = attr.GetTextEffectFlags();
  m_paragraphSpacingAfter = attr.GetParagraphSpacingAfter();
  m_paragraphSpacingBefore = attr.GetParagraphSpacingBefore();
  m_lineSpacing = attr.GetLineSpacing();
  m_characterStyleName = attr.GetCharacterStyleName();
  m_paragraphStyleName = attr.GetParagraphStyleName();
  m_listStyleName = attr.GetListStyleName();
  m_bulletStyle = attr.GetBulletStyle();
  m_bulletNumber = attr.GetBulletNumber();
  m_bulletText = attr.GetBulletText();
  m_bulletName = attr.GetBulletName();
  m_bulletFont = attr.GetBulletFont();
  m_outlineLevel = attr.GetOutlineLevel();
  m_urlTarget = attr.GetURL();
  if( attr.GetFont().Ok() ) {
    GetFontAttributes( attr.GetFont() );
  }
}

// Making a wxTextAttrEx object.
wxRichTextAttr::operator wxTextAttrEx() const {
  wxTextAttrEx attr;
  attr.SetTextColour( GetTextColour() );
  attr.SetBackgroundColour( GetBackgroundColour() );
  attr.SetAlignment( GetAlignment() );
  attr.SetTabs( GetTabs() );
  attr.SetLeftIndent( GetLeftIndent(), GetLeftSubIndent() );
  attr.SetRightIndent( GetRightIndent() );
  if( HasFont() ) {
    attr.SetFont( CreateFont() );
  }
  attr.SetParagraphSpacingAfter( m_paragraphSpacingAfter );
  attr.SetParagraphSpacingBefore( m_paragraphSpacingBefore );
  attr.SetLineSpacing( m_lineSpacing );
  attr.SetBulletStyle( m_bulletStyle );
  attr.SetBulletNumber( m_bulletNumber );
  attr.SetBulletText( m_bulletText );
  attr.SetBulletName( m_bulletName );
  attr.SetBulletFont( m_bulletFont );
  attr.SetCharacterStyleName( m_characterStyleName );
  attr.SetParagraphStyleName( m_paragraphStyleName );
  attr.SetListStyleName( m_listStyleName );
  attr.SetTextEffects( m_textEffects );
  attr.SetTextEffectFlags( m_textEffectFlags );
  attr.SetOutlineLevel( m_outlineLevel );
  attr.SetURL( m_urlTarget );
  attr.SetFlags( GetFlags() ); // Important: set after SetFont and others, since they set flags
  return attr;
}

// Equality test
bool wxRichTextAttr::operator== ( const wxRichTextAttr& attr ) const {
  return  GetFlags() == attr.GetFlags() &&
          GetTextColour() == attr.GetTextColour() &&
          GetBackgroundColour() == attr.GetBackgroundColour() &&
          GetAlignment() == attr.GetAlignment() &&
          GetLeftIndent() == attr.GetLeftIndent() &&
          GetLeftSubIndent() == attr.GetLeftSubIndent() &&
          GetRightIndent() == attr.GetRightIndent() &&
          wxRichTextTabsEq( GetTabs(), attr.GetTabs() ) &&
          GetParagraphSpacingAfter() == attr.GetParagraphSpacingAfter() &&
          GetParagraphSpacingBefore() == attr.GetParagraphSpacingBefore() &&
          GetLineSpacing() == attr.GetLineSpacing() &&
          GetCharacterStyleName() == attr.GetCharacterStyleName() &&
          GetParagraphStyleName() == attr.GetParagraphStyleName() &&
          GetListStyleName() == attr.GetListStyleName() &&
          GetBulletStyle() == attr.GetBulletStyle() &&
          GetBulletText() == attr.GetBulletText() &&
          GetBulletNumber() == attr.GetBulletNumber() &&
          GetBulletFont() == attr.GetBulletFont() &&
          GetBulletName() == attr.GetBulletName() &&
          GetTextEffects() == attr.GetTextEffects() &&
          GetTextEffectFlags() == attr.GetTextEffectFlags() &&
          GetOutlineLevel() == attr.GetOutlineLevel() &&
          GetFontSize() == attr.GetFontSize() &&
          GetFontStyle() == attr.GetFontStyle() &&
          GetFontWeight() == attr.GetFontWeight() &&
          GetFontUnderlined() == attr.GetFontUnderlined() &&
          GetFontFaceName() == attr.GetFontFaceName() &&
          GetURL() == attr.GetURL();
}

// Create font from font attributes.
wxFont wxRichTextAttr::CreateFont() const {
  wxFont font( m_fontSize, wxDEFAULT, m_fontStyle, m_fontWeight, m_fontUnderlined, m_fontFaceName );
  return font;
}

// Get attributes from font.
bool wxRichTextAttr::GetFontAttributes( const wxFont& font ) {
  if( !font.Ok() ) {
    return false;
  }
  m_fontSize = font.GetPointSize();
  m_fontStyle = font.GetStyle();
  m_fontWeight = font.GetWeight();
  m_fontUnderlined = font.GetUnderlined();
  m_fontFaceName = font.GetFaceName();
  return true;
}

wxRichTextAttr wxRichTextAttr::Combine( const wxRichTextAttr& style, const wxRichTextAttr* compareWith ) const {
  wxRichTextAttr destStyle = ( *this );
  destStyle.Apply( style, compareWith );
  return destStyle;
}

bool wxRichTextAttr::Apply( const wxRichTextAttr& style, const wxRichTextAttr* compareWith ) {
  wxRichTextAttr& destStyle = ( *this );
  if( style.HasFontWeight() ) {
    if( !( compareWith && compareWith->HasFontWeight() && compareWith->GetFontWeight() == style.GetFontWeight() ) ) {
      destStyle.SetFontWeight( style.GetFontWeight() );
    }
  }
  if( style.HasFontSize() ) {
    if( !( compareWith && compareWith->HasFontSize() && compareWith->GetFontSize() == style.GetFontSize() ) ) {
      destStyle.SetFontSize( style.GetFontSize() );
    }
  }
  if( style.HasFontItalic() ) {
    if( !( compareWith && compareWith->HasFontItalic() && compareWith->GetFontStyle() == style.GetFontStyle() ) ) {
      destStyle.SetFontStyle( style.GetFontStyle() );
    }
  }
  if( style.HasFontUnderlined() ) {
    if( !( compareWith && compareWith->HasFontUnderlined() && compareWith->GetFontUnderlined() == style.GetFontUnderlined() ) ) {
      destStyle.SetFontUnderlined( style.GetFontUnderlined() );
    }
  }
  if( style.HasFontFaceName() ) {
    if( !( compareWith && compareWith->HasFontFaceName() && compareWith->GetFontFaceName() == style.GetFontFaceName() ) ) {
      destStyle.SetFontFaceName( style.GetFontFaceName() );
    }
  }
  if( style.GetTextColour().Ok() && style.HasTextColour() ) {
    if( !( compareWith && compareWith->HasTextColour() && compareWith->GetTextColour() == style.GetTextColour() ) ) {
      destStyle.SetTextColour( style.GetTextColour() );
    }
  }
  if( style.GetBackgroundColour().Ok() && style.HasBackgroundColour() ) {
    if( !( compareWith && compareWith->HasBackgroundColour() && compareWith->GetBackgroundColour() == style.GetBackgroundColour() ) ) {
      destStyle.SetBackgroundColour( style.GetBackgroundColour() );
    }
  }
  if( style.HasAlignment() ) {
    if( !( compareWith && compareWith->HasAlignment() && compareWith->GetAlignment() == style.GetAlignment() ) ) {
      destStyle.SetAlignment( style.GetAlignment() );
    }
  }
  if( style.HasTabs() ) {
    if( !( compareWith && compareWith->HasTabs() && wxRichTextTabsEq( compareWith->GetTabs(), style.GetTabs() ) ) ) {
      destStyle.SetTabs( style.GetTabs() );
    }
  }
  if( style.HasLeftIndent() ) {
    if( !( compareWith && compareWith->HasLeftIndent() && compareWith->GetLeftIndent() == style.GetLeftIndent()
           && compareWith->GetLeftSubIndent() == style.GetLeftSubIndent() ) ) {
      destStyle.SetLeftIndent( style.GetLeftIndent(), style.GetLeftSubIndent() );
    }
  }
  if( style.HasRightIndent() ) {
    if( !( compareWith && compareWith->HasRightIndent() && compareWith->GetRightIndent() == style.GetRightIndent() ) ) {
      destStyle.SetRightIndent( style.GetRightIndent() );
    }
  }
  if( style.HasParagraphSpacingAfter() ) {
    if( !( compareWith && compareWith->HasParagraphSpacingAfter() && compareWith->GetParagraphSpacingAfter() == style.GetParagraphSpacingAfter() ) ) {
      destStyle.SetParagraphSpacingAfter( style.GetParagraphSpacingAfter() );
    }
  }
  if( style.HasParagraphSpacingBefore() ) {
    if( !( compareWith && compareWith->HasParagraphSpacingBefore() && compareWith->GetParagraphSpacingBefore() == style.GetParagraphSpacingBefore() ) ) {
      destStyle.SetParagraphSpacingBefore( style.GetParagraphSpacingBefore() );
    }
  }
  if( style.HasLineSpacing() ) {
    if( !( compareWith && compareWith->HasLineSpacing() && compareWith->GetLineSpacing() == style.GetLineSpacing() ) ) {
      destStyle.SetLineSpacing( style.GetLineSpacing() );
    }
  }
  if( style.HasCharacterStyleName() ) {
    if( !( compareWith && compareWith->HasCharacterStyleName() && compareWith->GetCharacterStyleName() == style.GetCharacterStyleName() ) ) {
      destStyle.SetCharacterStyleName( style.GetCharacterStyleName() );
    }
  }
  if( style.HasParagraphStyleName() ) {
    if( !( compareWith && compareWith->HasParagraphStyleName() && compareWith->GetParagraphStyleName() == style.GetParagraphStyleName() ) ) {
      destStyle.SetParagraphStyleName( style.GetParagraphStyleName() );
    }
  }
  if( style.HasListStyleName() ) {
    if( !( compareWith && compareWith->HasListStyleName() && compareWith->GetListStyleName() == style.GetListStyleName() ) ) {
      destStyle.SetListStyleName( style.GetListStyleName() );
    }
  }
  if( style.HasBulletStyle() ) {
    if( !( compareWith && compareWith->HasBulletStyle() && compareWith->GetBulletStyle() == style.GetBulletStyle() ) ) {
      destStyle.SetBulletStyle( style.GetBulletStyle() );
    }
  }
  if( style.HasBulletText() ) {
    if( !( compareWith && compareWith->HasBulletText() && compareWith->GetBulletText() == style.GetBulletText() ) ) {
      destStyle.SetBulletText( style.GetBulletText() );
      destStyle.SetBulletFont( style.GetBulletFont() );
    }
  }
  if( style.HasBulletNumber() ) {
    if( !( compareWith && compareWith->HasBulletNumber() && compareWith->GetBulletNumber() == style.GetBulletNumber() ) ) {
      destStyle.SetBulletNumber( style.GetBulletNumber() );
    }
  }
  if( style.HasBulletName() ) {
    if( !( compareWith && compareWith->HasBulletName() && compareWith->GetBulletName() == style.GetBulletName() ) ) {
      destStyle.SetBulletName( style.GetBulletName() );
    }
  }
  if( style.HasURL() ) {
    if( !( compareWith && compareWith->HasURL() && compareWith->GetURL() == style.GetURL() ) ) {
      destStyle.SetURL( style.GetURL() );
    }
  }
  if( style.HasPageBreak() ) {
    if( !( compareWith && compareWith->HasPageBreak() ) ) {
      destStyle.SetPageBreak();
    }
  }
  if( style.HasTextEffects() ) {
    if( !( compareWith && compareWith->HasTextEffects() && compareWith->GetTextEffects() == style.GetTextEffects() ) ) {
      int destBits = destStyle.GetTextEffects();
      int destFlags = destStyle.GetTextEffectFlags();
      int srcBits = style.GetTextEffects();
      int srcFlags = style.GetTextEffectFlags();
      wxRichTextCombineBitlists( destBits, srcBits, destFlags, srcFlags );
      destStyle.SetTextEffects( destBits );
      destStyle.SetTextEffectFlags( destFlags );
    }
  }
  if( style.HasOutlineLevel() ) {
    if( !( compareWith && compareWith->HasOutlineLevel() && compareWith->GetOutlineLevel() == style.GetOutlineLevel() ) ) {
      destStyle.SetOutlineLevel( style.GetOutlineLevel() );
    }
  }
  return true;
}

/*!
  wxTextAttrEx is an extended version of wxTextAttr with more paragraph attributes.
*/

wxTextAttrEx::wxTextAttrEx( const wxTextAttrEx& attr ): wxTextAttr() {
  Copy( attr );
}

// Initialise this object.
void wxTextAttrEx::Init() {
  m_paragraphSpacingAfter = 0;
  m_paragraphSpacingBefore = 0;
  m_lineSpacing = 0;
  m_bulletStyle = wxTEXT_ATTR_BULLET_STYLE_NONE;
  m_textEffects = wxTEXT_ATTR_EFFECT_NONE;
  m_textEffectFlags = wxTEXT_ATTR_EFFECT_NONE;
  m_bulletNumber = 0;
  m_outlineLevel = 0;
}

// Copy
void wxTextAttrEx::Copy( const wxTextAttrEx& attr ) {
  wxTextAttr::operator= ( attr );
  m_paragraphSpacingAfter = attr.m_paragraphSpacingAfter;
  m_paragraphSpacingBefore = attr.m_paragraphSpacingBefore;
  m_lineSpacing = attr.m_lineSpacing;
  m_characterStyleName = attr.m_characterStyleName;
  m_paragraphStyleName = attr.m_paragraphStyleName;
  m_listStyleName = attr.m_listStyleName;
  m_bulletStyle = attr.m_bulletStyle;
  m_bulletNumber = attr.m_bulletNumber;
  m_bulletText = attr.m_bulletText;
  m_bulletFont = attr.m_bulletFont;
  m_bulletName = attr.m_bulletName;
  m_urlTarget = attr.m_urlTarget;
  m_textEffects = attr.m_textEffects;
  m_textEffectFlags = attr.m_textEffectFlags;
  m_outlineLevel = attr.m_outlineLevel;
}

// Assignment from a wxTextAttrEx object
void wxTextAttrEx::operator= ( const wxTextAttrEx& attr ) {
  Copy( attr );
}

// Assignment from a wxTextAttr object.
void wxTextAttrEx::operator= ( const wxTextAttr& attr ) {
  wxTextAttr::operator= ( attr );
}

// Equality test
bool wxTextAttrEx::operator== ( const wxTextAttrEx& attr ) const {
  return (
           GetFlags() == attr.GetFlags() &&
           GetTextColour() == attr.GetTextColour() &&
           GetBackgroundColour() == attr.GetBackgroundColour() &&
           GetFont() == attr.GetFont() &&
           GetTextEffects() == attr.GetTextEffects() &&
           GetTextEffectFlags() == attr.GetTextEffectFlags() &&
           GetAlignment() == attr.GetAlignment() &&
           GetLeftIndent() == attr.GetLeftIndent() &&
           GetRightIndent() == attr.GetRightIndent() &&
           GetLeftSubIndent() == attr.GetLeftSubIndent() &&
           wxRichTextTabsEq( GetTabs(), attr.GetTabs() ) &&
           GetLineSpacing() == attr.GetLineSpacing() &&
           GetParagraphSpacingAfter() == attr.GetParagraphSpacingAfter() &&
           GetParagraphSpacingBefore() == attr.GetParagraphSpacingBefore() &&
           GetBulletStyle() == attr.GetBulletStyle() &&
           GetBulletNumber() == attr.GetBulletNumber() &&
           GetBulletText() == attr.GetBulletText() &&
           GetBulletName() == attr.GetBulletName() &&
           GetBulletFont() == attr.GetBulletFont() &&
           GetCharacterStyleName() == attr.GetCharacterStyleName() &&
           GetParagraphStyleName() == attr.GetParagraphStyleName() &&
           GetListStyleName() == attr.GetListStyleName() &&
           GetOutlineLevel() == attr.GetOutlineLevel() &&
           GetURL() == attr.GetURL() );
}

wxTextAttrEx wxTextAttrEx::CombineEx( const wxTextAttrEx& attr,
                                      const wxTextAttrEx& attrDef,
                                      const wxTextCtrlBase *text ) {
  wxTextAttrEx newAttr;
  // If attr specifies the complete font, just use that font, overriding all
  // default font attributes.
  if( ( attr.GetFlags() & wxTEXT_ATTR_FONT ) == wxTEXT_ATTR_FONT ) {
    newAttr.SetFont( attr.GetFont() );
  } else {
    // First find the basic, default font
    long flags = 0;
    wxFont font;
    if( attrDef.HasFont() ) {
      flags = ( attrDef.GetFlags() & wxTEXT_ATTR_FONT );
      font = attrDef.GetFont();
    } else {
      if( text ) {
        font = text->GetFont();
      }
      // We leave flags at 0 because no font attributes have been specified yet
    }
    if( !font.Ok() ) {
      font = *wxNORMAL_FONT;
    }
    // Otherwise, if there are font attributes in attr, apply them
    if( attr.GetFlags() & wxTEXT_ATTR_FONT ) {
      if( attr.HasFontSize() ) {
        flags |= wxTEXT_ATTR_FONT_SIZE;
        wxFontSetPointSize( font, attr.GetFont().GetPointSize() );
      }
      if( attr.HasFontItalic() ) {
        flags |= wxTEXT_ATTR_FONT_ITALIC;;
        wxFontSetStyle( font, attr.GetFont().GetStyle() );
      }
      if( attr.HasFontWeight() ) {
        flags |= wxTEXT_ATTR_FONT_WEIGHT;
        wxFontSetWeight( font, attr.GetFont().GetWeight() );
      }
      if( attr.HasFontFaceName() ) {
        flags |= wxTEXT_ATTR_FONT_FACE;
        wxFontSetFaceName( font, attr.GetFont().GetFaceName() );
      }
      if( attr.HasFontUnderlined() ) {
        flags |= wxTEXT_ATTR_FONT_UNDERLINE;
        wxFontSetUnderlined( font, attr.GetFont().GetUnderlined() );
      }
      newAttr.SetFont( font );
      newAttr.SetFlags( newAttr.GetFlags() | flags );
    }
  }
  wxColour colFg = attr.GetTextColour();
  if( !colFg.Ok() ) {
    colFg = attrDef.GetTextColour();
    if( text && !colFg.Ok() ) {
      colFg = text->GetForegroundColour();
    }
  }
  wxColour colBg = attr.GetBackgroundColour();
  if( !colBg.Ok() ) {
    colBg = attrDef.GetBackgroundColour();
    if( text && !colBg.Ok() ) {
      colBg = text->GetBackgroundColour();
    }
  }
  newAttr.SetTextColour( colFg );
  newAttr.SetBackgroundColour( colBg );
  if( attr.HasAlignment() ) {
    newAttr.SetAlignment( attr.GetAlignment() );
  } else if( attrDef.HasAlignment() ) {
    newAttr.SetAlignment( attrDef.GetAlignment() );
  }
  if( attr.HasTabs() ) {
    newAttr.SetTabs( attr.GetTabs() );
  } else if( attrDef.HasTabs() ) {
    newAttr.SetTabs( attrDef.GetTabs() );
  }
  if( attr.HasLeftIndent() ) {
    newAttr.SetLeftIndent( attr.GetLeftIndent(), attr.GetLeftSubIndent() );
  } else if( attrDef.HasLeftIndent() ) {
    newAttr.SetLeftIndent( attrDef.GetLeftIndent(), attr.GetLeftSubIndent() );
  }
  if( attr.HasRightIndent() ) {
    newAttr.SetRightIndent( attr.GetRightIndent() );
  } else if( attrDef.HasRightIndent() ) {
    newAttr.SetRightIndent( attrDef.GetRightIndent() );
  }
  // NEW ATTRIBUTES
  if( attr.HasParagraphSpacingAfter() ) {
    newAttr.SetParagraphSpacingAfter( attr.GetParagraphSpacingAfter() );
  }
  if( attr.HasParagraphSpacingBefore() ) {
    newAttr.SetParagraphSpacingBefore( attr.GetParagraphSpacingBefore() );
  }
  if( attr.HasLineSpacing() ) {
    newAttr.SetLineSpacing( attr.GetLineSpacing() );
  }
  if( attr.HasCharacterStyleName() ) {
    newAttr.SetCharacterStyleName( attr.GetCharacterStyleName() );
  }
  if( attr.HasParagraphStyleName() ) {
    newAttr.SetParagraphStyleName( attr.GetParagraphStyleName() );
  }
  if( attr.HasListStyleName() ) {
    newAttr.SetListStyleName( attr.GetListStyleName() );
  }
  if( attr.HasBulletStyle() ) {
    newAttr.SetBulletStyle( attr.GetBulletStyle() );
  }
  if( attr.HasBulletNumber() ) {
    newAttr.SetBulletNumber( attr.GetBulletNumber() );
  }
  if( attr.HasBulletName() ) {
    newAttr.SetBulletName( attr.GetBulletName() );
  }
  if( attr.HasBulletText() ) {
    newAttr.SetBulletText( attr.GetBulletText() );
    newAttr.SetBulletFont( attr.GetBulletFont() );
  }
  if( attr.HasURL() ) {
    newAttr.SetURL( attr.GetURL() );
  }
  if( attr.HasTextEffects() ) {
    newAttr.SetTextEffects( attr.GetTextEffects() );
    newAttr.SetTextEffectFlags( attr.GetTextEffectFlags() );
  }
  if( attr.HasOutlineLevel() ) {
    newAttr.SetOutlineLevel( attr.GetOutlineLevel() );
  }
  return newAttr;
}


/*!
  wxRichTextFileHandler
  Base class for file handlers
*/

IMPLEMENT_CLASS( wxRichTextFileHandler, wxObject )

bool wxRichTextFileHandler::LoadFile( wxRichTextBuffer *buffer, const wxString& filename ) {
  wxFFileInputStream stream( filename );
  if( stream.Ok() ) {
    return LoadFile( buffer, stream );
  }
  return false;
}

bool wxRichTextFileHandler::SaveFile( wxRichTextBuffer *buffer, const wxString& filename ) {
  wxFFileOutputStream stream( filename );
  if( stream.Ok() ) {
    return SaveFile( buffer, stream );
  }
  return false;
}

/// Can we handle this filename (if using files)? By default, checks the extension.
bool wxRichTextFileHandler::CanHandle( const wxString& filename ) const {
  wxString path, file, ext;
  wxSplitPath( filename, & path, & file, & ext );
  return ( ext.Lower() == GetExtension() );
}

/*!
  wxRichTextTextHandler
  Plain text handler
*/

IMPLEMENT_CLASS( wxRichTextPlainTextHandler, wxRichTextFileHandler )

bool wxRichTextPlainTextHandler::DoLoadFile( wxRichTextBuffer *buffer, wxInputStream& stream ) {
  if( !stream.IsOk() ) {
    return false;
  }
  wxString str;
  int lastCh = 0;
  while( !stream.Eof() ) {
    int ch = stream.GetC();
    if( !stream.Eof() ) {
      if( ch == 10 && lastCh != 13 ) {
        str += wxT( '\n' );
      }
      if( ch > 0 && ch != 10 ) {
        str += wxChar( ch );
      }
      lastCh = ch;
    }
  }
  buffer->ResetAndClearCommands();
  buffer->Clear();
  buffer->AddParagraphs( str );
  buffer->UpdateRanges();
  return true;
}

bool wxRichTextPlainTextHandler::DoSaveFile( wxRichTextBuffer *buffer, wxOutputStream& stream ) {
  if( !stream.IsOk() ) {
    return false;
  }
  wxString text = buffer->GetText();
  wxString newLine = wxRichTextLineBreakChar;
  text.Replace( newLine, wxT( "\n" ) );
  wxCharBuffer buf = text.ToAscii();
  stream.Write( ( const char* ) buf, text.length() );
  return true;
}

/*
   Stores information about an image, in binary in-memory form
*/

wxRichTextImageBlock::wxRichTextImageBlock() {
  Init();
}

wxRichTextImageBlock::wxRichTextImageBlock( const wxRichTextImageBlock& block ): wxObject() {
  Init();
  Copy( block );
}

wxRichTextImageBlock::~wxRichTextImageBlock() {
  if( m_data ) {
    delete[] m_data;
    m_data = NULL;
  }
}

void wxRichTextImageBlock::Init() {
  m_data = NULL;
  m_dataSize = 0;
  m_imageType = -1;
}

void wxRichTextImageBlock::Clear() {
  delete[] m_data;
  m_data = NULL;
  m_dataSize = 0;
  m_imageType = -1;
}


// Load the original image into a memory block.
// If the image is not a JPEG, we must convert it into a JPEG
// to conserve space.
// If it's not a JPEG we can make use of 'image', already scaled, so we don't have to
// load the image a 2nd time.

bool wxRichTextImageBlock::MakeImageBlock( const wxString& filename, int imageType, wxImage& image, bool convertToJPEG ) {
  m_imageType = imageType;
  wxString filenameToRead( filename );
  bool removeFile = false;
  if( imageType == -1 ) {
    return false;
  }
  // Could not determine image type
  if( ( imageType != wxBITMAP_TYPE_JPEG ) && convertToJPEG ) {
    wxString tempFile;
    bool success = wxGetTempFileName( _( "image" ), tempFile ) ;
    wxASSERT( success );
    wxUnusedVar( success );
    image.SaveFile( tempFile, wxBITMAP_TYPE_JPEG );
    filenameToRead = tempFile;
    removeFile = true;
    m_imageType = wxBITMAP_TYPE_JPEG;
  }
  wxFile file;
  if( !file.Open( filenameToRead ) ) {
    return false;
  }
  m_dataSize = ( size_t ) file.Length();
  file.Close();
  if( m_data ) {
    delete[] m_data;
  }
  m_data = ReadBlock( filenameToRead, m_dataSize );
  if( removeFile ) {
    wxRemoveFile( filenameToRead );
  }
  return ( m_data != NULL );
}

// Make an image block from the wxImage in the given
// format.
bool wxRichTextImageBlock::MakeImageBlock( wxImage& image, int imageType, int quality ) {
  m_imageType = imageType;
  image.SetOption( wxT( "quality" ), quality );
  if( imageType == -1 ) {
    return false;
  }
  // Could not determine image type
  wxString tempFile;
  bool success = wxGetTempFileName( _( "image" ), tempFile ) ;
  wxASSERT( success );
  wxUnusedVar( success );
  if( !image.SaveFile( tempFile, m_imageType ) ) {
    if( wxFileExists( tempFile ) ) {
      wxRemoveFile( tempFile );
    }
    return false;
  }
  wxFile file;
  if( !file.Open( tempFile ) ) {
    return false;
  }
  m_dataSize = ( size_t ) file.Length();
  file.Close();
  if( m_data ) {
    delete[] m_data;
  }
  m_data = ReadBlock( tempFile, m_dataSize );
  wxRemoveFile( tempFile );
  return ( m_data != NULL );
}


// Write to a file
bool wxRichTextImageBlock::Write( const wxString& filename ) {
  return WriteBlock( filename, m_data, m_dataSize );
}

void wxRichTextImageBlock::Copy( const wxRichTextImageBlock& block ) {
  m_imageType = block.m_imageType;
  if( m_data ) {
    delete[] m_data;
    m_data = NULL;
  }
  m_dataSize = block.m_dataSize;
  if( m_dataSize == 0 ) {
    return;
  }
  m_data = new unsigned char[m_dataSize];
  unsigned int i;
  for( i = 0; i < m_dataSize; i++ ) {
    m_data[i] = block.m_data[i];
  }
}

//// Operators
void wxRichTextImageBlock::operator=( const wxRichTextImageBlock& block ) {
  Copy( block );
}

// Load a wxImage from the block
bool wxRichTextImageBlock::Load( wxImage& image ) {
  if( !m_data ) {
    return false;
  }
  // Read in the image.
  wxMemoryInputStream mstream( m_data, m_dataSize );
  bool success = image.LoadFile( mstream, GetImageType() );
  return success;
}

// Array used in DecToHex conversion routine.
static char hexArray[] = "0123456789ABCDEF";

// Convert decimal integer to 2-character hex string
inline void wxRichTextDecToHex( int dec, char* buf ) {
  int firstDigit = ( int )( dec / 16.0 );
  int secondDigit = ( int )( dec - ( firstDigit * 16.0 ) );
  buf[0] = hexArray[firstDigit];
  buf[1] = hexArray[secondDigit];
}

inline int wxRichTextHexToDec( const wxChar* buf ) {
  int firstDigit, secondDigit;
  if( buf[0] >= wxT( 'A' ) ) {
    firstDigit = buf[0] - wxT( 'A' ) + 10;
  } else
  { firstDigit = buf[0] - wxT( '0' ); }
  if( buf[1] >= wxT( 'A' ) ) {
    secondDigit = buf[1] - wxT( 'A' ) + 10;
  } else
  { secondDigit = buf[1] - wxT( '0' ); }
  return ( firstDigit & 0xF ) * 16 + ( secondDigit & 0xF );
}


// Write data in hex to a stream
bool wxRichTextImageBlock::WriteHex( wxOutputStream& stream ) {
  const int bufSize = 512;
  char buf[bufSize + 1];
  int left = m_dataSize;
  int n, i, j;
  j = 0;
  while( left > 0 ) {
    if( left * 2 > bufSize ) {
      n = bufSize;
      left -= ( bufSize / 2 );
    } else {
      n = left * 2;
      left = 0;
    }
    char* b = buf;
    for( i = 0; i < ( n / 2 ); i++ ) {
      wxRichTextDecToHex( m_data[j], b );
      b += 2;
      j ++;
    }
    buf[n] = 0;
    stream.Write( ( const char* ) buf, n );
  }
  return true;
}

// Read data in hex from a stream
bool wxRichTextImageBlock::ReadHex( wxInputStream& stream, int length, int imageType ) {
  int dataSize = length / 2;
  if( m_data ) {
    delete[] m_data;
  }
  wxChar str[2];
  m_data = new unsigned char[dataSize];
  int i;
  for( i = 0; i < dataSize; i ++ ) {
    str[0] = stream.GetC();
    str[1] = stream.GetC();
    m_data[i] = ( unsigned char )wxRichTextHexToDec( str );
  }
  m_dataSize = dataSize;
  m_imageType = imageType;
  return true;
}

unsigned char* wxRichTextImageBlock::ReadBlock( wxInputStream& stream, size_t size ) {
  unsigned char* block = new unsigned char[size];
  if( !block ) {
    return NULL;
  }
  stream.Read( block, size );
  return block;
}

unsigned char* wxRichTextImageBlock::ReadBlock( const wxString& filename, size_t size ) {
  wxFileInputStream stream( filename );
  if( !stream.Ok() ) {
    return NULL;
  }
  return ReadBlock( stream, size );
}

bool wxRichTextImageBlock::WriteBlock( wxOutputStream& stream, unsigned char* block, size_t size ) {
  stream.Write( ( void* ) block, size );
  return stream.IsOk();
}

bool wxRichTextImageBlock::WriteBlock( const wxString& filename, unsigned char* block, size_t size ) {
  wxFileOutputStream outStream( filename );
  if( !outStream.Ok() ) {
    return false;
  }
  return WriteBlock( outStream, block, size );
}

wxString wxRichTextImageBlock::GetExtension() const {
  wxImageHandler* handler = wxImage::FindHandler( GetImageType() );
  if( handler ) {
    return handler->GetExtension();
  } else
  { return wxEmptyString; }
}

const wxChar *wxRichTextBufferDataObject::ms_richTextBufferFormatId = wxT( "wxShape" );

wxRichTextBufferDataObject::wxRichTextBufferDataObject( wxRichTextBuffer* richTextBuffer ) {
  m_richTextBuffer = richTextBuffer;
  m_formatRichTextBuffer.SetId( GetRichTextBufferFormatId() );
  SetFormat( m_formatRichTextBuffer );
}

wxRichTextBufferDataObject::~wxRichTextBufferDataObject() {
  delete m_richTextBuffer;
}

wxRichTextBuffer* wxRichTextBufferDataObject::GetRichTextBuffer() {
  wxRichTextBuffer* richTextBuffer = m_richTextBuffer;
  m_richTextBuffer = NULL;
  return richTextBuffer;
}

wxDataFormat wxRichTextBufferDataObject::GetPreferredFormat( Direction ( dir ) ) const {
  return m_formatRichTextBuffer;
}

size_t wxRichTextBufferDataObject::GetDataSize() const {
  if( !m_richTextBuffer ) {
    return 0;
  }
  wxString bufXML;
  {
    wxStringOutputStream stream( & bufXML );
    if( !m_richTextBuffer->SaveFile( stream, wxRICHTEXT_TYPE_XML ) ) {
      wxLogError( wxT( "Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler." ) );
      return 0;
    }
  }
  wxCharBuffer buffer = bufXML.mb_str( wxConvUTF8 );
  return strlen( buffer ) + 1;
}

bool wxRichTextBufferDataObject::GetDataHere( void *pBuf ) const {
  if( !pBuf || !m_richTextBuffer ) {
    return false;
  }
  wxString bufXML;
  {
    wxStringOutputStream stream( & bufXML );
    if( !m_richTextBuffer->SaveFile( stream, wxRICHTEXT_TYPE_XML ) ) {
      wxLogError( wxT( "Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler." ) );
      return 0;
    }
  }
  wxCharBuffer buffer = bufXML.mb_str( wxConvUTF8 );
  size_t len = strlen( buffer );
  memcpy( ( char* ) pBuf, ( const char* ) buffer, len );
  ( ( char* ) pBuf )[len] = 0;
  return true;
}

bool wxRichTextBufferDataObject::SetData( size_t ( len ), const void *buf ) {
  delete m_richTextBuffer;
  m_richTextBuffer = NULL;
  wxString bufXML( ( const char* ) buf, wxConvUTF8 );
  m_richTextBuffer = new wxRichTextBuffer;
  wxStringInputStream stream( bufXML );
  if( !m_richTextBuffer->LoadFile( stream, wxRICHTEXT_TYPE_XML ) ) {
    wxLogError( wxT( "Could not read the buffer from an XML stream.\nYou may have forgotten to add the XML file handler." ) );
    delete m_richTextBuffer;
    m_richTextBuffer = NULL;
    return false;
  }
  return true;
}
